WIP - Pydantic v2 support (#1238)
* WIP * WIP - make test_model_definition tests pass * WIP - make test_model_methods pass * WIP - make whole test suit at least run - failing 49/443 tests * WIP fix part of the getting pydantic tests as types of fields are now kept in core schema and not on fieldsinfo * WIP fix validation in update by creating individual fields validators, failing 36/443 * WIP fix __pydantic_extra__ in intializing model, fix test related to pydantic config checks, failing 32/442 * WIP - fix enum schema in model_json_schema, failing 31/442 * WIP - fix copying through model, fix setting pydantic fields on through, fix default config and inheriting from it, failing 26/442 * WIP fix tests checking pydantic schema, fix excluding parent fields, failing 21/442 * WIP some missed files * WIP - fix validators inheritance and fix validators in generated pydantic, failing 17/442 * WIP - fix through models setting - only on reverse side of relation, but always on reverse side, failing 15/442 * WIP - fix through models setting - only on reverse side of relation, but always on reverse side, failing 15/442 * WIP - working on proper populating __dict__ for relations for new schema dumping, some work on openapi docs, failing 13/442 * WIP - remove property fields as pydantic has now computed_field on its own, failing 9/442 * WIP - fixes in docs, failing 8/442 * WIP - fix tests for largebinary schema, wrapped bytes fields fail in pydantic, will be fixed in pydantic-core, remaining is circural schema for related models, failing 6/442 * WIP - fix to pk only models in schemas * Getting test suites to pass (#1249) * wip, fixing tests * iteration, fixing some more tests * iteration, fixing some more tests * adhere to comments * adhere to comments * remove unnecessary dict call, re-add getattribute for testing * todo for reverse relationship * adhere to comments, remove prints * solve circular refs * all tests pass 🎉 * remove 3.7 from tests * add lint and type check jobs * reforat with ruff, fix jobs * rename jobs * fix imports * fix evaluate in py3.8 * partially fix coverage * fix coverage, add more tests * fix test ids * fix test ids * fix lint, fix docs, make docs fully working scripts, add test docs job * fix pyproject * pin py ver in test docs * change dir in test docs * fix pydantic warning hack * rm poetry call in test_docs * switch to pathlib in test docs * remove coverage req test docs * fix type check tests, fix part of types * fix/skip next part of types * fix next part of types * fix next part of types * fix coverage * fix coverage * fix type (bit dirty 🤷) * fix some code smells * change pre-commit * tweak workflows * remove no root from tests * switch to full python path by passing sys.executable * some small refactor in new base model, one sample test, change makefile * small refactors to reduce complexity of methods * temp add tests for prs against pydantic_v2 * remove all references to __fields__ * remove all references to construct, deprecate the method and update model_construct to be in line with pydantic * deprecate dict and add model_dump, todo switch to model_dict in calls * fix tests * change to union * change to union * change to model_dump and model_dump_json from dict and json deprecated methods, deprecate them in ormar too * finish switching dict() -> model_dump() * finish switching json() -> model_dump_json() * remove fully pydantic_only * switch to extra for payment card, change missed json calls * fix coverage - no more warnings internal * fix coverage - no more warnings internal - part 2 * split model_construct into own and pydantic parts * split determine pydantic field type * change to new field validators * fix benchmarks, add codspeed instead of pytest-benchmark, add action and gh workflow * restore pytest-benchmark * remove codspeed * pin pydantic version, restore codspeed * change on push to pydantic_v2 to trigger first one * Use lifespan function instead of event (#1259) * check return types * fix imports order, set warnings=False on json that passes the dict, fix unnecessary loop in one of the test * remove references to model's meta as it's now ormar config, rename related methods too * filter out pydantic serializer warnings * remove choices leftovers * remove leftovers after property_fields, keep only enough to exclude them in initialization * add migration guide * fix meta references * downgrade databases for now * Change line numbers in documentation (#1265) * proofread and fix the docs, part 1 * proofread and fix the docs for models * proofread and fix the docs for fields * proofread and fix the docs for relations * proofread and fix rest of the docs, add release notes for 0.20 * create tables in new docs src * cleanup old deps, uncomment docs publish on tag * fix import reorder --------- Co-authored-by: TouwaStar <30479449+TouwaStar@users.noreply.github.com> Co-authored-by: Goran Mekić <meka@tilda.center>
This commit is contained in:
6
.flake8
6
.flake8
@ -1,6 +0,0 @@
|
|||||||
[flake8]
|
|
||||||
ignore = ANN101, ANN102, W503, S101, CFQ004, S311
|
|
||||||
max-complexity = 8
|
|
||||||
max-line-length = 88
|
|
||||||
import-order-style = pycharm
|
|
||||||
exclude = p38venv,.pytest_cache
|
|
||||||
6
.github/workflows/deploy-docs.yml
vendored
6
.github/workflows/deploy-docs.yml
vendored
@ -27,6 +27,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo $RELEASE_VERSION
|
echo $RELEASE_VERSION
|
||||||
echo ${{ env.RELEASE_VERSION }}
|
echo ${{ env.RELEASE_VERSION }}
|
||||||
# - name: Deploy
|
- name: Deploy
|
||||||
# run: |
|
run: |
|
||||||
# mike deploy --push --update-aliases ${{ env.RELEASE_VERSION }} latest
|
mike deploy --push --update-aliases ${{ env.RELEASE_VERSION }} latest
|
||||||
|
|||||||
44
.github/workflows/lint.yml
vendored
Normal file
44
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'gh-pages'
|
||||||
|
pull_request:
|
||||||
|
branches: [ master, pydantic_v2 ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: "Python ${{ matrix.python-version }}"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'collerek/ormar'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1.3.3
|
||||||
|
with:
|
||||||
|
version: 1.4.2
|
||||||
|
virtualenvs-create: false
|
||||||
|
|
||||||
|
- name: Poetry details
|
||||||
|
run: |
|
||||||
|
poetry --version
|
||||||
|
poetry config --list
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: poetry install --extras "all" --no-root
|
||||||
|
|
||||||
|
- name: Format
|
||||||
|
run: make fmt
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: make lint
|
||||||
62
.github/workflows/test-package.yml
vendored
62
.github/workflows/test-package.yml
vendored
@ -1,14 +1,14 @@
|
|||||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
name: build
|
name: test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'gh-pages'
|
- 'gh-pages'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [ master, pydantic_v2 ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
@ -17,7 +17,7 @@ jobs:
|
|||||||
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'collerek/ormar'
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'collerek/ormar'
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.7, 3.8, 3.9, "3.10", 3.11]
|
python-version: [3.8, 3.9, "3.10", 3.11]
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
@ -39,35 +39,75 @@ jobs:
|
|||||||
POSTGRES_DB: testsuite
|
POSTGRES_DB: testsuite
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 --name postgres
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: false
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1.3.3
|
||||||
|
with:
|
||||||
|
version: 1.4.2
|
||||||
|
virtualenvs-create: false
|
||||||
|
|
||||||
|
- name: Poetry details
|
||||||
run: |
|
run: |
|
||||||
python -m pip install poetry==1.4.2
|
poetry --version
|
||||||
poetry install --extras "all"
|
poetry config --list
|
||||||
env:
|
|
||||||
POETRY_VIRTUALENVS_CREATE: false
|
- name: Install dependencies
|
||||||
|
run: poetry install --extras "all"
|
||||||
|
|
||||||
- name: Run mysql
|
- name: Run mysql
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "mysql://username:password@127.0.0.1:3306/testsuite"
|
DATABASE_URL: "mysql://username:password@127.0.0.1:3306/testsuite"
|
||||||
run: bash scripts/test.sh
|
run: bash scripts/test.sh
|
||||||
|
|
||||||
|
- name: Install postgresql-client
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install --yes postgresql-client
|
||||||
|
|
||||||
|
- name: Connect to PostgreSQL with CLI
|
||||||
|
run: env PGPASSWORD=password psql -h localhost -U username -c 'SELECT VERSION();' testsuite
|
||||||
|
|
||||||
|
- name: Show max connections
|
||||||
|
run: env PGPASSWORD=password psql -h localhost -U username -c 'SHOW max_connections;' testsuite
|
||||||
|
|
||||||
|
- name: Alter max connections
|
||||||
|
run: |
|
||||||
|
|
||||||
|
docker exec -i postgres bash << EOF
|
||||||
|
sed -i -e 's/max_connections = 100/max_connections = 1000/' /var/lib/postgresql/data/postgresql.conf
|
||||||
|
sed -i -e 's/shared_buffers = 128MB/shared_buffers = 512MB/' /var/lib/postgresql/data/postgresql.conf
|
||||||
|
EOF
|
||||||
|
docker restart --time 0 postgres
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
- name: Show max connections
|
||||||
|
run: env PGPASSWORD=password psql -h localhost -U username -c 'SHOW max_connections;' testsuite
|
||||||
|
|
||||||
- name: Run postgres
|
- name: Run postgres
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "postgresql://username:password@localhost:5432/testsuite"
|
DATABASE_URL: "postgresql://username:password@localhost:5432/testsuite"
|
||||||
run: bash scripts/test.sh
|
run: bash scripts/test.sh
|
||||||
|
|
||||||
- name: Run sqlite
|
- name: Run sqlite
|
||||||
env:
|
env:
|
||||||
DATABASE_URL: "sqlite:///testsuite"
|
DATABASE_URL: "sqlite:///testsuite"
|
||||||
run: bash scripts/test.sh
|
run: bash scripts/test.sh
|
||||||
- run: mypy ormar tests benchmarks
|
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
uses: codecov/codecov-action@v3.1.6
|
uses: codecov/codecov-action@v3.1.6
|
||||||
|
|
||||||
- name: Test & publish code coverage
|
- name: Test & publish code coverage
|
||||||
uses: paambaati/codeclimate-action@v5.0.0
|
uses: paambaati/codeclimate-action@v5.0.0
|
||||||
if: github.event.pull_request.head.repo.full_name == 'collerek/ormar'
|
if: github.event.pull_request.head.repo.full_name == 'collerek/ormar'
|
||||||
|
|||||||
31
.github/workflows/test_docs.yml
vendored
Normal file
31
.github/workflows/test_docs.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: test_docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'gh-pages'
|
||||||
|
pull_request:
|
||||||
|
branches: [ master, pydantic_v2 ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
tests_docs:
|
||||||
|
name: "Python ${{ matrix.python-version }}"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'collerek/ormar'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install poetry==1.4.2
|
||||||
|
poetry install --extras "all"
|
||||||
|
env:
|
||||||
|
POETRY_VIRTUALENVS_CREATE: false
|
||||||
|
- name: Test docs
|
||||||
|
run: bash scripts/test_docs.sh
|
||||||
41
.github/workflows/type-check.yml
vendored
Normal file
41
.github/workflows/type-check.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||||
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||||
|
|
||||||
|
name: type_check
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- 'gh-pages'
|
||||||
|
pull_request:
|
||||||
|
branches: [ master, pydantic_v2 ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: "Python ${{ matrix.python-version }}"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != 'collerek/ormar'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
|
||||||
|
- name: Install Poetry
|
||||||
|
uses: snok/install-poetry@v1.3.3
|
||||||
|
with:
|
||||||
|
version: 1.4.2
|
||||||
|
virtualenvs-create: false
|
||||||
|
|
||||||
|
- name: Poetry details
|
||||||
|
run: |
|
||||||
|
poetry --version
|
||||||
|
poetry config --list
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: poetry install --extras "all" --no-root
|
||||||
|
|
||||||
|
- name: Type check
|
||||||
|
run: make type_check
|
||||||
@ -1,31 +1,9 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
- repo: local
|
||||||
rev: 22.3.0
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: pre-commit-local
|
||||||
exclude: ^(docs_src/|examples/)
|
name: format
|
||||||
- repo: https://github.com/pycqa/flake8
|
entry: make pre-commit
|
||||||
rev: 3.9.2
|
language: python
|
||||||
hooks:
|
pass_filenames: false
|
||||||
- id: flake8
|
|
||||||
exclude: ^(docs_src/|examples/|tests/)
|
|
||||||
args: [ '--max-line-length=88' ]
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
||||||
rev: v0.982
|
|
||||||
hooks:
|
|
||||||
- id: mypy
|
|
||||||
exclude: ^(docs_src/|examples/)
|
|
||||||
args: [--no-strict-optional, --ignore-missing-imports]
|
|
||||||
additional_dependencies: [
|
|
||||||
types-ujson>=0.1.1,
|
|
||||||
types-PyMySQL>=1.0.2,
|
|
||||||
types-ipaddress>=1.0.0,
|
|
||||||
types-enum34>=1.1.0,
|
|
||||||
types-cryptography>=3.3.5,
|
|
||||||
types-orjson>=3.6.0,
|
|
||||||
types-aiofiles>=0.1.9,
|
|
||||||
types-pkg-resources>=0.1.3,
|
|
||||||
types-requests>=2.25.9,
|
|
||||||
types-toml>=0.10.0,
|
|
||||||
pydantic>=1.8.2
|
|
||||||
]
|
|
||||||
|
|||||||
20
Makefile
20
Makefile
@ -15,18 +15,22 @@ test_mysql:
|
|||||||
test_sqlite:
|
test_sqlite:
|
||||||
bash scripts/test.sh -svv
|
bash scripts/test.sh -svv
|
||||||
|
|
||||||
|
test_docs:
|
||||||
|
bash scripts/test_docs.sh -svv
|
||||||
|
|
||||||
test:
|
test:
|
||||||
pytest
|
pytest -svv tests/
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
pytest --cov=ormar --cov=tests --cov-fail-under=100 --cov-report=term-missing
|
pytest --cov=ormar --cov=tests --cov-fail-under=100 --cov-report=term-missing tests
|
||||||
|
|
||||||
black:
|
type_check:
|
||||||
black ormar tests
|
mkdir -p .mypy_cache && poetry run python -m mypy ormar tests --ignore-missing-imports --install-types --non-interactive
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
black ormar tests
|
poetry run python -m ruff . --fix
|
||||||
flake8 ormar
|
|
||||||
|
|
||||||
mypy:
|
fmt:
|
||||||
mypy ormar tests
|
poetry run python -m black .
|
||||||
|
|
||||||
|
pre-commit: fmt lint type_check
|
||||||
35
README.md
35
README.md
@ -171,39 +171,31 @@ import ormar
|
|||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
DATABASE_URL = "sqlite:///db.sqlite"
|
DATABASE_URL = "sqlite:///db.sqlite"
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
engine=sqlalchemy.create_engine(DATABASE_URL),
|
||||||
# note that this step is optional -> all ormar cares is a internal
|
)
|
||||||
# class with name Meta and proper parameters, but this way you do not
|
|
||||||
# have to repeat the same parameters if you use only one database
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
# Note that all type hints are optional
|
# Note that all type hints are optional
|
||||||
# below is a perfectly valid model declaration
|
# below is a perfectly valid model declaration
|
||||||
# class Author(ormar.Model):
|
# class Author(ormar.Model):
|
||||||
# class Meta(BaseMeta):
|
# ormar_config = base_ormar_config.copy(tablename="authors")
|
||||||
# tablename = "authors"
|
|
||||||
#
|
#
|
||||||
# id = ormar.Integer(primary_key=True) # <= notice no field types
|
# id = ormar.Integer(primary_key=True) # <= notice no field types
|
||||||
# name = ormar.String(max_length=100)
|
# name = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="authors")
|
||||||
tablename = "authors"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="books")
|
||||||
tablename = "books"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
@ -214,10 +206,9 @@ class Book(ormar.Model):
|
|||||||
# create the database
|
# create the database
|
||||||
# note that in production you should use migrations
|
# note that in production you should use migrations
|
||||||
# note that this is not required if you connect to existing database
|
# note that this is not required if you connect to existing database
|
||||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
|
||||||
# just to be sure we clear the db before
|
# just to be sure we clear the db before
|
||||||
metadata.drop_all(engine)
|
base_ormar_config.metadata.drop_all(base_ormar_config.engine)
|
||||||
metadata.create_all(engine)
|
base_ormar_config.metadata.create_all(base_ormar_config.engine)
|
||||||
|
|
||||||
|
|
||||||
# all functions below are divided into functionality categories
|
# all functions below are divided into functionality categories
|
||||||
@ -546,7 +537,7 @@ async def raw_data():
|
|||||||
async def with_connect(function):
|
async def with_connect(function):
|
||||||
# note that for any other backend than sqlite you actually need to
|
# note that for any other backend than sqlite you actually need to
|
||||||
# connect to the database to perform db operations
|
# connect to the database to perform db operations
|
||||||
async with database:
|
async with base_ormar_config.database:
|
||||||
await function()
|
await function()
|
||||||
|
|
||||||
# note that if you use framework like `fastapi` you shouldn't connect
|
# note that if you use framework like `fastapi` you shouldn't connect
|
||||||
@ -576,7 +567,7 @@ for func in [
|
|||||||
asyncio.run(with_connect(func))
|
asyncio.run(with_connect(func))
|
||||||
|
|
||||||
# drop the database tables
|
# drop the database tables
|
||||||
metadata.drop_all(engine)
|
base_ormar_config.metadata.drop_all(base_ormar_config.engine)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ormar Specification
|
## Ormar Specification
|
||||||
@ -654,7 +645,6 @@ The following keyword arguments are supported on all field types.
|
|||||||
* `unique: bool`
|
* `unique: bool`
|
||||||
* `choices: typing.Sequence`
|
* `choices: typing.Sequence`
|
||||||
* `name: str`
|
* `name: str`
|
||||||
* `pydantic_only: bool`
|
|
||||||
|
|
||||||
All fields are required unless one of the following is set:
|
All fields are required unless one of the following is set:
|
||||||
|
|
||||||
@ -664,7 +654,6 @@ All fields are required unless one of the following is set:
|
|||||||
* `server_default` - Set a default value for the field on server side (like sqlalchemy's `func.now()`). **Not available for relation fields**
|
* `server_default` - Set a default value for the field on server side (like sqlalchemy's `func.now()`). **Not available for relation fields**
|
||||||
* `primary key` with `autoincrement` - When a column is set to primary key and autoincrement is set on this column.
|
* `primary key` with `autoincrement` - When a column is set to primary key and autoincrement is set on this column.
|
||||||
Autoincrement is set by default on int primary keys.
|
Autoincrement is set by default on int primary keys.
|
||||||
* `pydantic_only` - Field is available only as normal pydantic field, not stored in the database.
|
|
||||||
|
|
||||||
### Available signals
|
### Available signals
|
||||||
|
|
||||||
|
|||||||
@ -3,31 +3,20 @@ import random
|
|||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import databases
|
|
||||||
import nest_asyncio
|
import nest_asyncio
|
||||||
|
import ormar
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
import sqlalchemy
|
from tests.lifespan import init_tests
|
||||||
|
from tests.settings import create_config
|
||||||
import ormar
|
|
||||||
from tests.settings import DATABASE_URL
|
|
||||||
|
|
||||||
|
base_ormar_config = create_config()
|
||||||
nest_asyncio.apply()
|
nest_asyncio.apply()
|
||||||
|
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL)
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
pytestmark = pytest.mark.asyncio
|
pytestmark = pytest.mark.asyncio
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="authors")
|
||||||
tablename = "authors"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -41,8 +30,7 @@ class AuthorWithManyFields(Author):
|
|||||||
|
|
||||||
|
|
||||||
class Publisher(ormar.Model):
|
class Publisher(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="publishers")
|
||||||
tablename = "publishers"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -50,8 +38,7 @@ class Publisher(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="books")
|
||||||
tablename = "books"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Author = ormar.ForeignKey(Author, index=True)
|
author: Author = ormar.ForeignKey(Author, index=True)
|
||||||
@ -60,13 +47,7 @@ class Book(ormar.Model):
|
|||||||
year: int = ormar.Integer(nullable=True)
|
year: int = ormar.Integer(nullable=True)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True, scope="function") # TODO: fix this to be module
|
create_test_database = init_tests(base_ormar_config, scope="function")
|
||||||
def create_test_database():
|
|
||||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
|
||||||
metadata.drop_all(engine)
|
|
||||||
metadata.create_all(engine)
|
|
||||||
yield
|
|
||||||
metadata.drop_all(engine)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
@ -86,7 +67,7 @@ async def authors_in_db(num_models: int):
|
|||||||
authors = [
|
authors = [
|
||||||
Author(
|
Author(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
for i in range(0, num_models)
|
for i in range(0, num_models)
|
||||||
]
|
]
|
||||||
|
|||||||
@ -15,7 +15,7 @@ async def test_making_and_inserting_models_in_bulk(aio_benchmark, num_models: in
|
|||||||
authors = [
|
authors = [
|
||||||
Author(
|
Author(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
for i in range(0, num_models)
|
for i in range(0, num_models)
|
||||||
]
|
]
|
||||||
|
|||||||
@ -16,7 +16,7 @@ async def test_creating_models_individually(aio_benchmark, num_models: int):
|
|||||||
for idx in range(0, num_models):
|
for idx in range(0, num_models):
|
||||||
author = await Author.objects.create(
|
author = await Author.objects.create(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
authors.append(author)
|
authors.append(author)
|
||||||
return authors
|
return authors
|
||||||
@ -62,7 +62,7 @@ async def test_get_or_create_when_create(aio_benchmark, num_models: int):
|
|||||||
for idx in range(0, num_models):
|
for idx in range(0, num_models):
|
||||||
author, created = await Author.objects.get_or_create(
|
author, created = await Author.objects.get_or_create(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
assert created
|
assert created
|
||||||
authors.append(author)
|
authors.append(author)
|
||||||
@ -81,7 +81,7 @@ async def test_update_or_create_when_create(aio_benchmark, num_models: int):
|
|||||||
for idx in range(0, num_models):
|
for idx in range(0, num_models):
|
||||||
author = await Author.objects.update_or_create(
|
author = await Author.objects.update_or_create(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
authors.append(author)
|
authors.append(author)
|
||||||
return authors
|
return authors
|
||||||
|
|||||||
@ -15,13 +15,13 @@ async def test_initializing_models(aio_benchmark, num_models: int):
|
|||||||
authors = [
|
authors = [
|
||||||
Author(
|
Author(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
for i in range(0, num_models)
|
for i in range(0, num_models)
|
||||||
]
|
]
|
||||||
assert len(authors) == num_models
|
assert len(authors) == num_models
|
||||||
|
|
||||||
initialize_models(num_models)
|
_ = initialize_models(num_models)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("num_models", [10, 20, 40])
|
@pytest.mark.parametrize("num_models", [10, 20, 40])
|
||||||
|
|||||||
@ -15,7 +15,7 @@ async def test_saving_models_individually(aio_benchmark, num_models: int):
|
|||||||
authors = [
|
authors = [
|
||||||
Author(
|
Author(
|
||||||
name="".join(random.sample(string.ascii_letters, 5)),
|
name="".join(random.sample(string.ascii_letters, 5)),
|
||||||
score=random.random() * 100,
|
score=int(random.random() * 100),
|
||||||
)
|
)
|
||||||
for i in range(0, num_models)
|
for i in range(0, num_models)
|
||||||
]
|
]
|
||||||
|
|||||||
@ -26,7 +26,8 @@ Here you can find a very simple sample application code.
|
|||||||
|
|
||||||
### Imports and initialization
|
### Imports and initialization
|
||||||
|
|
||||||
First take care of the imports and initialization
|
Define startup and shutdown procedures using FastAPI lifespan and use is in the
|
||||||
|
application.
|
||||||
```python
|
```python
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
@ -36,29 +37,26 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
|
||||||
app = FastAPI()
|
from contextlib import asynccontextmanager
|
||||||
metadata = sqlalchemy.MetaData()
|
from fastapi import FastAPI
|
||||||
database = databases.Database("sqlite:///test.db")
|
|
||||||
app.state.database = database
|
|
||||||
```
|
|
||||||
|
|
||||||
### Database connection
|
|
||||||
|
|
||||||
Next define startup and shutdown events (or use middleware)
|
|
||||||
- note that this is `databases` specific setting not the ormar one
|
|
||||||
```python
|
|
||||||
@app.on_event("startup")
|
|
||||||
async def startup() -> None:
|
|
||||||
database_ = app.state.database
|
|
||||||
if not database_.is_connected:
|
|
||||||
await database_.connect()
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("shutdown")
|
@asynccontextmanager
|
||||||
async def shutdown() -> None:
|
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
||||||
database_ = app.state.database
|
if not config.database.is_connected:
|
||||||
if database_.is_connected:
|
await config.database.connect()
|
||||||
await database_.disconnect()
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
if config.database.is_connected:
|
||||||
|
await config.database.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database("sqlite:///test.db"),
|
||||||
|
)
|
||||||
|
app = FastAPI(lifespan=lifespan(base_ormar_config))
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!info
|
!!!info
|
||||||
@ -71,21 +69,21 @@ Define ormar models with appropriate fields.
|
|||||||
Those models will be used instead of pydantic ones.
|
Those models will be used instead of pydantic ones.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
base_ormar_config = OrmarConfig(
|
||||||
class Meta:
|
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
metadata = metadata
|
||||||
database = database
|
database = database
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Category(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -122,7 +120,7 @@ async def create_category(category: Category):
|
|||||||
@app.put("/items/{item_id}")
|
@app.put("/items/{item_id}")
|
||||||
async def get_item(item_id: int, item: Item):
|
async def get_item(item_id: int, item: Item):
|
||||||
item_db = await Item.objects.get(pk=item_id)
|
item_db = await Item.objects.get(pk=item_id)
|
||||||
return await item_db.update(**item.dict())
|
return await item_db.update(**item.model_dump())
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/items/{item_id}")
|
@app.delete("/items/{item_id}")
|
||||||
@ -197,14 +195,14 @@ def test_all_endpoints():
|
|||||||
assert items[0] == item
|
assert items[0] == item
|
||||||
|
|
||||||
item.name = "New name"
|
item.name = "New name"
|
||||||
response = client.put(f"/items/{item.pk}", json=item.dict())
|
response = client.put(f"/items/{item.pk}", json=item.model_dump())
|
||||||
assert response.json() == item.dict()
|
assert response.json() == item.model_dump()
|
||||||
|
|
||||||
response = client.get("/items/")
|
response = client.get("/items/")
|
||||||
items = [Item(**item) for item in response.json()]
|
items = [Item(**item) for item in response.json()]
|
||||||
assert items[0].name == "New name"
|
assert items[0].name == "New name"
|
||||||
|
|
||||||
response = client.delete(f"/items/{item.pk}", json=item.dict())
|
response = client.delete(f"/items/{item.pk}", json=item.model_dump())
|
||||||
assert response.json().get("deleted_rows", "__UNDEFINED__") != "__UNDEFINED__"
|
assert response.json().get("deleted_rows", "__UNDEFINED__") != "__UNDEFINED__"
|
||||||
response = client.get("/items/")
|
response = client.get("/items/")
|
||||||
items = response.json()
|
items = response.json()
|
||||||
|
|||||||
@ -23,11 +23,13 @@ Field is not required if (any/many/all) of following:
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
class User(ormar.Model):
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
class Meta:
|
|
||||||
tablename: str = "users"
|
|
||||||
metadata=metadata
|
metadata=metadata
|
||||||
database=database
|
database=database
|
||||||
|
)
|
||||||
|
|
||||||
|
class User(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy()
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
email: str = ormar.String(max_length=255)
|
email: str = ormar.String(max_length=255)
|
||||||
@ -66,14 +68,14 @@ RequestUser = User.get_pydantic(exclude={"password": ..., "category": {"priority
|
|||||||
@app.post("/users3/", response_model=User) # here you can also use both ormar/pydantic
|
@app.post("/users3/", response_model=User) # here you can also use both ormar/pydantic
|
||||||
async def create_user3(user: RequestUser): # use the generated model here
|
async def create_user3(user: RequestUser): # use the generated model here
|
||||||
# note how now user is pydantic and not ormar Model so you need to convert
|
# note how now user is pydantic and not ormar Model so you need to convert
|
||||||
return await User(**user.dict()).save()
|
return await User(**user.model_dump()).save()
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!Note
|
!!!Note
|
||||||
To see more examples and read more visit [get_pydantic](../models/methods.md#get_pydantic) part of the documentation.
|
To see more examples and read more visit [get_pydantic](../models/methods.md#get_pydantic) part of the documentation.
|
||||||
|
|
||||||
!!!Warning
|
!!!Warning
|
||||||
The `get_pydantic` method generates all models in a tree of nested models according to an algorithm that allows to avoid loops in models (same algorithm that is used in `dict()`, `select_all()` etc.)
|
The `get_pydantic` method generates all models in a tree of nested models according to an algorithm that allows to avoid loops in models (same algorithm that is used in `model_dump()`, `select_all()` etc.)
|
||||||
|
|
||||||
That means that nested models won't have reference to parent model (by default ormar relation is bidirectional).
|
That means that nested models won't have reference to parent model (by default ormar relation is bidirectional).
|
||||||
|
|
||||||
@ -94,7 +96,7 @@ RequestUser = User.get_pydantic(exclude={"password": ..., "category": {"priority
|
|||||||
@app.post("/users3/", response_model=User)
|
@app.post("/users3/", response_model=User)
|
||||||
async def create_user3(user: RequestUser): # type: ignore
|
async def create_user3(user: RequestUser): # type: ignore
|
||||||
# note how now user is not ormar Model so you need to convert
|
# note how now user is not ormar Model so you need to convert
|
||||||
return await User(**user.dict()).save()
|
return await User(**user.model_dump()).save()
|
||||||
```
|
```
|
||||||
|
|
||||||
The second one is a little bit more hacky and utilizes a way in which fastapi extract function parameters.
|
The second one is a little bit more hacky and utilizes a way in which fastapi extract function parameters.
|
||||||
@ -105,7 +107,7 @@ You can overwrite the `__annotations__` entry for given param.
|
|||||||
RequestUser = User.get_pydantic(exclude={"password": ..., "category": {"priority"}})
|
RequestUser = User.get_pydantic(exclude={"password": ..., "category": {"priority"}})
|
||||||
# do not use the app decorator
|
# do not use the app decorator
|
||||||
async def create_user3(user: User): # use ormar model here
|
async def create_user3(user: User): # use ormar model here
|
||||||
return await User(**user.dict()).save()
|
return await User(**user.model_dump()).save()
|
||||||
# overwrite the function annotations entry for user param with generated model
|
# overwrite the function annotations entry for user param with generated model
|
||||||
create_user3.__annotations__["user"] = RequestUser
|
create_user3.__annotations__["user"] = RequestUser
|
||||||
# manually call app functions (app.get, app.post etc.) and pass your function reference
|
# manually call app functions (app.get, app.post etc.) and pass your function reference
|
||||||
@ -126,8 +128,7 @@ Sample:
|
|||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
class UserCreate(pydantic.BaseModel):
|
class UserCreate(pydantic.BaseModel):
|
||||||
class Config:
|
model_config = pydantic.ConfigDict(from_attributes=True)
|
||||||
orm_mode = True
|
|
||||||
|
|
||||||
email: str
|
email: str
|
||||||
first_name: str
|
first_name: str
|
||||||
@ -139,5 +140,5 @@ class UserCreate(pydantic.BaseModel):
|
|||||||
async def create_user3(user: UserCreate): # use pydantic model here
|
async def create_user3(user: UserCreate): # use pydantic model here
|
||||||
# note how now request param is a pydantic model and not the ormar one
|
# note how now request param is a pydantic model and not the ormar one
|
||||||
# so you need to parse/convert it to ormar before you can use database
|
# so you need to parse/convert it to ormar before you can use database
|
||||||
return await User(**user.dict()).save()
|
return await User(**user.model_dump()).save()
|
||||||
```
|
```
|
||||||
|
|||||||
@ -22,11 +22,13 @@ Field is not required if (any/many/all) of following:
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
```python
|
```python
|
||||||
class User(ormar.Model):
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
class Meta:
|
|
||||||
tablename: str = "users"
|
|
||||||
metadata=metadata
|
metadata=metadata
|
||||||
database=database
|
database=database
|
||||||
|
)
|
||||||
|
|
||||||
|
class User(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy()
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
email: str = ormar.String(max_length=255)
|
email: str = ormar.String(max_length=255)
|
||||||
@ -111,13 +113,13 @@ One is a dictionary with nested fields that represents the model tree structure,
|
|||||||
Assume for a second that our user's category is a separate model:
|
Assume for a second that our user's category is a separate model:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class BaseMeta(ormar.ModelMeta):
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata=metadata
|
metadata=metadata
|
||||||
database=database
|
database=database
|
||||||
|
)
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename: str = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=255)
|
name: str = ormar.String(max_length=255)
|
||||||
@ -125,8 +127,7 @@ class Category(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class User(ormar.Model):
|
class User(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename: str = "users"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
email: str = ormar.String(max_length=255)
|
email: str = ormar.String(max_length=255)
|
||||||
@ -153,7 +154,7 @@ In example `response_model_exclude={"category__priority", "category__other_field
|
|||||||
Note that apart from `response_model_exclude` parameter `fastapi` supports also other parameters inherited from `pydantic`.
|
Note that apart from `response_model_exclude` parameter `fastapi` supports also other parameters inherited from `pydantic`.
|
||||||
All of them works also with ormar, but can have some nuances so best to read [dict](../models/methods.md#dict) part of the documentation.
|
All of them works also with ormar, but can have some nuances so best to read [dict](../models/methods.md#dict) part of the documentation.
|
||||||
|
|
||||||
### Exclude in `Model.dict()`
|
### Exclude in `Model.model_dump()`
|
||||||
|
|
||||||
Alternatively you can just return a dict from `ormar.Model` and use .
|
Alternatively you can just return a dict from `ormar.Model` and use .
|
||||||
|
|
||||||
@ -166,14 +167,14 @@ Like this you can also set exclude/include as dict and exclude fields on nested
|
|||||||
@app.post("/users2/", response_model=User)
|
@app.post("/users2/", response_model=User)
|
||||||
async def create_user2(user: User):
|
async def create_user2(user: User):
|
||||||
user = await user.save()
|
user = await user.save()
|
||||||
return user.dict(exclude={'password'})
|
return user.model_dump(exclude={'password'})
|
||||||
# could be also something like return user.dict(exclude={'category': {'priority'}}) to exclude category priority
|
# could be also something like return user.model_dump(exclude={'category': {'priority'}}) to exclude category priority
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!Note
|
!!!Note
|
||||||
Note that above example will nullify the password field even if you pass it in request, but the **field will be still there** as it's part of the response schema, the value will be set to `None`.
|
Note that above example will nullify the password field even if you pass it in request, but the **field will be still there** as it's part of the response schema, the value will be set to `None`.
|
||||||
|
|
||||||
If you want to fully exclude the field with this approach simply don't use `response_model` and exclude in Model's dict()
|
If you want to fully exclude the field with this approach simply don't use `response_model` and exclude in Model's model_dump()
|
||||||
|
|
||||||
Alternatively you can just return a dict from ormar model.
|
Alternatively you can just return a dict from ormar model.
|
||||||
Like this you can also set exclude/include as dict and exclude fields on nested models.
|
Like this you can also set exclude/include as dict and exclude fields on nested models.
|
||||||
@ -187,7 +188,7 @@ So if you skip `response_model` altogether you can do something like this:
|
|||||||
@app.post("/users4/") # note no response_model
|
@app.post("/users4/") # note no response_model
|
||||||
async def create_user4(user: User):
|
async def create_user4(user: User):
|
||||||
user = await user.save()
|
user = await user.save()
|
||||||
return user.dict(exclude={'last_name'})
|
return user.model_dump(exclude={'last_name'})
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!Note
|
!!!Note
|
||||||
@ -213,7 +214,7 @@ async def create_user3(user: User):
|
|||||||
To see more examples and read more visit [get_pydantic](../models/methods.md#get_pydantic) part of the documentation.
|
To see more examples and read more visit [get_pydantic](../models/methods.md#get_pydantic) part of the documentation.
|
||||||
|
|
||||||
!!!Warning
|
!!!Warning
|
||||||
The `get_pydantic` method generates all models in a tree of nested models according to an algorithm that allows to avoid loops in models (same algorithm that is used in `dict()`, `select_all()` etc.)
|
The `get_pydantic` method generates all models in a tree of nested models according to an algorithm that allows to avoid loops in models (same algorithm that is used in `model_dump()`, `select_all()` etc.)
|
||||||
|
|
||||||
That means that nested models won't have reference to parent model (by default ormar relation is bidirectional).
|
That means that nested models won't have reference to parent model (by default ormar relation is bidirectional).
|
||||||
|
|
||||||
@ -229,8 +230,7 @@ Sample:
|
|||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
class UserBase(pydantic.BaseModel):
|
class UserBase(pydantic.BaseModel):
|
||||||
class Config:
|
model_config = pydantic.ConfigDict(from_attributes=True)
|
||||||
orm_mode = True
|
|
||||||
|
|
||||||
email: str
|
email: str
|
||||||
first_name: str
|
first_name: str
|
||||||
|
|||||||
@ -29,7 +29,6 @@ Automatically changed to True if user provide one of the following:
|
|||||||
* `default` value or function is provided
|
* `default` value or function is provided
|
||||||
* `server_default` value or function is provided
|
* `server_default` value or function is provided
|
||||||
* `autoincrement` is set on `Integer` `primary_key` field
|
* `autoincrement` is set on `Integer` `primary_key` field
|
||||||
* **[DEPRECATED]**`pydantic_only=True` is set
|
|
||||||
|
|
||||||
Specifies if field is optional or required, used both with sql and pydantic.
|
Specifies if field is optional or required, used both with sql and pydantic.
|
||||||
|
|
||||||
@ -109,7 +108,7 @@ Used in sql only.
|
|||||||
|
|
||||||
Sample usage:
|
Sample usage:
|
||||||
|
|
||||||
```Python hl_lines="21-23"
|
```Python hl_lines="20-22"
|
||||||
--8<-- "../docs_src/fields/docs004.py"
|
--8<-- "../docs_src/fields/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -167,20 +166,6 @@ Sets the unique constraint on a table's column.
|
|||||||
|
|
||||||
Used in sql only.
|
Used in sql only.
|
||||||
|
|
||||||
## pydantic_only (**DEPRECATED**)
|
|
||||||
|
|
||||||
**This parameter is deprecated and will be removed in one of next releases!**
|
|
||||||
|
|
||||||
**To check how to declare pydantic only fields that are not saved into database see [pydantic fields section](pydantic-fields.md)**
|
|
||||||
|
|
||||||
`pydantic_only`: `bool` = `False`
|
|
||||||
|
|
||||||
Prevents creation of a sql column for given field.
|
|
||||||
|
|
||||||
Used for data related to given model but not to be stored in the database.
|
|
||||||
|
|
||||||
Used in pydantic only.
|
|
||||||
|
|
||||||
## overwrite_pydantic_type
|
## overwrite_pydantic_type
|
||||||
|
|
||||||
By default, ormar uses predefined pydantic field types that it applies on model creation (hence the type hints are optional).
|
By default, ormar uses predefined pydantic field types that it applies on model creation (hence the type hints are optional).
|
||||||
@ -199,12 +184,15 @@ So it's on you as a user to provide a type that is valid in the context of given
|
|||||||
As it's easy to break functionality of ormar the `overwrite_pydantic_type` argument is not available on relation fields!
|
As it's easy to break functionality of ormar the `overwrite_pydantic_type` argument is not available on relation fields!
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# sample overwrites
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
class OverwriteTest(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename = "overwrites"
|
|
||||||
metadata=metadata
|
metadata=metadata
|
||||||
database=database
|
database=database
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# sample overwrites
|
||||||
|
class OverwriteTest(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(tablename="overwrites")
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
my_int: str = ormar.Integer(overwrite_pydantic_type=PositiveInt)
|
my_int: str = ormar.Integer(overwrite_pydantic_type=PositiveInt)
|
||||||
@ -212,18 +200,6 @@ class OverwriteTest(ormar.Model):
|
|||||||
overwrite_pydantic_type=Optional[Json[Dict[str, int]]])
|
overwrite_pydantic_type=Optional[Json[Dict[str, int]]])
|
||||||
```
|
```
|
||||||
|
|
||||||
## choices
|
|
||||||
|
|
||||||
`choices`: `Sequence` = `[]`
|
|
||||||
|
|
||||||
A set of choices allowed to be used for given field.
|
|
||||||
|
|
||||||
Used for data validation on pydantic side.
|
|
||||||
|
|
||||||
Prevents insertion of value not present in the choices list.
|
|
||||||
|
|
||||||
Used in pydantic only.
|
|
||||||
|
|
||||||
[relations]: ../relations/index.md
|
[relations]: ../relations/index.md
|
||||||
[queries]: ../queries/index.md
|
[queries]: ../queries/index.md
|
||||||
[pydantic]: https://pydantic-docs.helpmanual.io/usage/types/#constrained-types
|
[pydantic]: https://pydantic-docs.helpmanual.io/usage/types/#constrained-types
|
||||||
|
|||||||
@ -17,10 +17,14 @@ well as both-way encryption/decryption (`FERNET` backend).
|
|||||||
|
|
||||||
To encrypt a field you need to pass at minimum `encrypt_secret` and `encrypt_backend` parameters.
|
To encrypt a field you need to pass at minimum `encrypt_secret` and `encrypt_backend` parameters.
|
||||||
|
|
||||||
```python hl_lines="7-8"
|
```python hl_lines="10-12"
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
metadata=metadata
|
||||||
|
database=database
|
||||||
|
)
|
||||||
|
|
||||||
class Filter(ormar.Model):
|
class Filter(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "filters"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100,
|
name: str = ormar.String(max_length=100,
|
||||||
@ -59,8 +63,7 @@ Note that since this backend never decrypt the stored value it's only applicable
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Hash(ormar.Model):
|
class Hash(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="hashes")
|
||||||
tablename = "hashes"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=128,
|
name: str = ormar.String(max_length=128,
|
||||||
@ -106,8 +109,7 @@ as the returned value is parsed to corresponding python type.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Filter(ormar.Model):
|
class Filter(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "filters"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100,
|
name: str = ormar.String(max_length=100,
|
||||||
@ -152,8 +154,7 @@ argument by `encrypt_custom_backend`.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Filter(ormar.Model):
|
class Filter(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "filters"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100,
|
name: str = ormar.String(max_length=100,
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
# Fields
|
# Fields
|
||||||
|
|
||||||
|
|
||||||
There are 12 basic model field types and a special `ForeignKey` and `Many2Many` fields to establish relationships between models.
|
There are 12 basic model field types and a special `ForeignKey` and `ManyToMany` fields to establish relationships between models.
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
For explanation of `ForeignKey` and `Many2Many` fields check [relations][relations].
|
For explanation of `ForeignKey` and `ManyToMany` fields check [relations][relations].
|
||||||
|
|
||||||
|
|
||||||
Each of the `Fields` has assigned both `sqlalchemy` column class and python type that is used to create `pydantic` model.
|
Each of the `Fields` has assigned both `sqlalchemy` column class and python type that is used to create `pydantic` model.
|
||||||
@ -160,11 +160,16 @@ That way you can i.e. set the value by API, even if value is not `utf-8` compati
|
|||||||
```python
|
```python
|
||||||
import base64
|
import base64
|
||||||
... # other imports skipped for brevity
|
... # other imports skipped for brevity
|
||||||
class LargeBinaryStr(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename = "my_str_blobs"
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata=metadata
|
metadata=metadata
|
||||||
database=database
|
database=database
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LargeBinaryStr(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(tablename="my_str_blobs")
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
test_binary: str = ormar.LargeBinary(
|
test_binary: str = ormar.LargeBinary(
|
||||||
@ -215,46 +220,6 @@ So which one to use depends on the backend you use and on the column/ data type
|
|||||||
* Sqlalchemy column: `sqlalchemy.Enum`
|
* Sqlalchemy column: `sqlalchemy.Enum`
|
||||||
* Type (used for pydantic): `Type[Enum]`
|
* Type (used for pydantic): `Type[Enum]`
|
||||||
|
|
||||||
#### Choices
|
|
||||||
You can change any field into `Enum` like field by passing a `choices` list that is accepted by all Field types.
|
|
||||||
|
|
||||||
It will add both: validation in `pydantic` model and will display available options in schema,
|
|
||||||
therefore it will be available in docs of `fastapi`.
|
|
||||||
|
|
||||||
If you still want to use `Enum` in your application you can do this by passing a `Enum` into choices
|
|
||||||
and later pass value of given option to a given field (note that Enum is not JsonSerializable).
|
|
||||||
|
|
||||||
```python
|
|
||||||
# note that imports and endpoints declaration
|
|
||||||
# is skipped here for brevity
|
|
||||||
from enum import Enum
|
|
||||||
class TestEnum(Enum):
|
|
||||||
val1 = 'Val1'
|
|
||||||
val2 = 'Val2'
|
|
||||||
|
|
||||||
class TestModel(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename = "org"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
|
||||||
# pass list(Enum) to choices
|
|
||||||
enum_string: str = ormar.String(max_length=100, choices=list(TestEnum))
|
|
||||||
|
|
||||||
# sample payload coming to fastapi
|
|
||||||
response = client.post(
|
|
||||||
"/test_models/",
|
|
||||||
json={
|
|
||||||
"id": 1,
|
|
||||||
# you need to refer to the value of the `Enum` option
|
|
||||||
# if called like this, alternatively just use value
|
|
||||||
# string "Val1" in this case
|
|
||||||
"enum_string": TestEnum.val1.value
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
[relations]: ../relations/index.md
|
[relations]: ../relations/index.md
|
||||||
[queries]: ../queries.md
|
[queries]: ../queries.md
|
||||||
|
|||||||
@ -22,17 +22,14 @@ If you set a field as `Optional`, it defaults to `None` if not provided and that
|
|||||||
exactly what's going to happen during loading from database.
|
exactly what's going to happen during loading from database.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
class ModelTest(ormar.Model):
|
class ModelTest(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200)
|
name: str = ormar.String(max_length=200)
|
||||||
@ -57,17 +54,14 @@ By setting a default value, this value will be set on initialization and databas
|
|||||||
Note that setting a default to `None` is the same as setting the field to `Optional`.
|
Note that setting a default to `None` is the same as setting the field to `Optional`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
class ModelTest(ormar.Model):
|
class ModelTest(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200)
|
name: str = ormar.String(max_length=200)
|
||||||
@ -97,13 +91,12 @@ on initialization and each database load.
|
|||||||
from pydantic import Field, PaymentCardNumber
|
from pydantic import Field, PaymentCardNumber
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL)
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
CARD_NUMBERS = [
|
CARD_NUMBERS = [
|
||||||
"123456789007",
|
"123456789007",
|
||||||
@ -119,8 +112,7 @@ def get_number():
|
|||||||
|
|
||||||
|
|
||||||
class ModelTest2(ormar.Model):
|
class ModelTest2(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200)
|
name: str = ormar.String(max_length=200)
|
||||||
@ -149,13 +141,12 @@ You can provide a value for the field in your `__init__()` method before calling
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL)
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
class PydanticTest(BaseModel):
|
class PydanticTest(BaseModel):
|
||||||
aa: str
|
aa: str
|
||||||
@ -163,8 +154,7 @@ class PydanticTest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class ModelTest3(ormar.Model):
|
class ModelTest3(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
# provide your custom init function
|
# provide your custom init function
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
### Overview
|
### Overview
|
||||||
|
|
||||||
The `ormar` package is an async mini ORM for Python, with support for **Postgres,
|
The `ormar` package is an async ORM for Python, with support for **Postgres,
|
||||||
MySQL**, and **SQLite**.
|
MySQL**, and **SQLite**.
|
||||||
|
|
||||||
The main benefits of using `ormar` are:
|
The main benefits of using `ormar` are:
|
||||||
@ -53,13 +53,7 @@ Yet remember that those are - well - tests and not all solutions are suitable to
|
|||||||
|
|
||||||
### Part of the `fastapi` ecosystem
|
### Part of the `fastapi` ecosystem
|
||||||
|
|
||||||
As part of the fastapi ecosystem `ormar` is supported in libraries that somehow work with databases.
|
As part of the fastapi ecosystem `ormar` is supported in selected libraries that somehow work with databases.
|
||||||
|
|
||||||
As of now `ormar` is supported by:
|
|
||||||
|
|
||||||
* [`fastapi-users`](https://github.com/frankie567/fastapi-users)
|
|
||||||
* [`fastapi-crudrouter`](https://github.com/awtkns/fastapi-crudrouter)
|
|
||||||
* [`fastapi-pagination`](https://github.com/uriyyo/fastapi-pagination)
|
|
||||||
|
|
||||||
Ormar remains sql dialect agnostic - so only columns working in all supported backends are implemented.
|
Ormar remains sql dialect agnostic - so only columns working in all supported backends are implemented.
|
||||||
|
|
||||||
@ -76,7 +70,6 @@ Ormar is built with:
|
|||||||
* [`sqlalchemy core`][sqlalchemy-core] for query building.
|
* [`sqlalchemy core`][sqlalchemy-core] for query building.
|
||||||
* [`databases`][databases] for cross-database async support.
|
* [`databases`][databases] for cross-database async support.
|
||||||
* [`pydantic`][pydantic] for data validation.
|
* [`pydantic`][pydantic] for data validation.
|
||||||
* `typing_extensions` for python 3.6 - 3.7
|
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
@ -128,17 +121,20 @@ For tests and basic applications the `sqlalchemy` is more than enough:
|
|||||||
# 1. Imports
|
# 1. Imports
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
|
|
||||||
# 2. Initialization
|
# 2. Initialization
|
||||||
DATABASE_URL = "sqlite:///db.sqlite"
|
DATABASE_URL = "sqlite:///db.sqlite"
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
engine=sqlalchemy.create_engine(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
# Define models here
|
# Define models here
|
||||||
|
|
||||||
# 3. Database creation and tables creation
|
# 3. Database creation and tables creation
|
||||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
base_ormar_config.metadata.create_all(engine)
|
||||||
metadata.create_all(engine)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
For a sample configuration of alembic and more information regarding migrations and
|
For a sample configuration of alembic and more information regarding migrations and
|
||||||
@ -175,45 +171,39 @@ Note that you can find the same script in examples folder on github.
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import pydantic
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
DATABASE_URL = "sqlite:///db.sqlite"
|
DATABASE_URL = "sqlite:///db.sqlite"
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
engine = sqlalchemy.create_engine(DATABASE_URL),
|
||||||
# note that this step is optional -> all ormar cares is a internal
|
)
|
||||||
# class with name Meta and proper parameters, but this way you do not
|
|
||||||
# have to repeat the same parameters if you use only one database
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
|
# note that this step is optional -> all ormar cares is a field with name
|
||||||
|
# ormar_config # and proper parameters, but this way you do not have to repeat
|
||||||
|
# the same parameters if you use only one database
|
||||||
|
#
|
||||||
# Note that all type hints are optional
|
# Note that all type hints are optional
|
||||||
# below is a perfectly valid model declaration
|
# below is a perfectly valid model declaration
|
||||||
# class Author(ormar.Model):
|
# class Author(ormar.Model):
|
||||||
# class Meta(BaseMeta):
|
# ormar_config = base_ormar_config.copy(tablename="authors")
|
||||||
# tablename = "authors"
|
|
||||||
#
|
#
|
||||||
# id = ormar.Integer(primary_key=True) # <= notice no field types
|
# id = ormar.Integer(primary_key=True) # <= notice no field types
|
||||||
# name = ormar.String(max_length=100)
|
# name = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="authors")
|
||||||
tablename = "authors"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="books")
|
||||||
tablename = "books"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
@ -224,10 +214,9 @@ class Book(ormar.Model):
|
|||||||
# create the database
|
# create the database
|
||||||
# note that in production you should use migrations
|
# note that in production you should use migrations
|
||||||
# note that this is not required if you connect to existing database
|
# note that this is not required if you connect to existing database
|
||||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
|
||||||
# just to be sure we clear the db before
|
# just to be sure we clear the db before
|
||||||
metadata.drop_all(engine)
|
base_ormar_config.metadata.drop_all(engine)
|
||||||
metadata.create_all(engine)
|
base_ormar_config.metadata.create_all(engine)
|
||||||
|
|
||||||
|
|
||||||
# all functions below are divided into functionality categories
|
# all functions below are divided into functionality categories
|
||||||
@ -662,9 +651,7 @@ The following keyword arguments are supported on all field types.
|
|||||||
* `server_default: Any`
|
* `server_default: Any`
|
||||||
* `index: bool`
|
* `index: bool`
|
||||||
* `unique: bool`
|
* `unique: bool`
|
||||||
* `choices: typing.Sequence`
|
|
||||||
* `name: str`
|
* `name: str`
|
||||||
* `pydantic_only: bool`
|
|
||||||
|
|
||||||
All fields are required unless one of the following is set:
|
All fields are required unless one of the following is set:
|
||||||
|
|
||||||
@ -674,7 +661,6 @@ All fields are required unless one of the following is set:
|
|||||||
* `server_default` - Set a default value for the field on server side (like sqlalchemy's `func.now()`). **Not available for relation fields**
|
* `server_default` - Set a default value for the field on server side (like sqlalchemy's `func.now()`). **Not available for relation fields**
|
||||||
* `primary key` with `autoincrement` - When a column is set to primary key and autoincrement is set on this column.
|
* `primary key` with `autoincrement` - When a column is set to primary key and autoincrement is set on this column.
|
||||||
Autoincrement is set by default on int primary keys.
|
Autoincrement is set by default on int primary keys.
|
||||||
* `pydantic_only` - Field is available only as normal pydantic field, not stored in the database.
|
|
||||||
|
|
||||||
### Available signals
|
### Available signals
|
||||||
|
|
||||||
|
|||||||
@ -13,22 +13,25 @@ Ormar uses `databases` for connectivity issues, `pydantic` for validation and `s
|
|||||||
All three should install along the installation of ormar if not present at your system before.
|
All three should install along the installation of ormar if not present at your system before.
|
||||||
|
|
||||||
* databases
|
* databases
|
||||||
* pydantic>=1.5
|
* pydantic
|
||||||
* sqlalchemy
|
* sqlalchemy
|
||||||
|
|
||||||
|
The required versions are pinned in the pyproject.toml file.
|
||||||
|
|
||||||
## Optional dependencies
|
## Optional dependencies
|
||||||
|
|
||||||
*ormar* has three optional dependencies based on database backend you use:
|
*ormar* has three optional dependencies based on database backend you use:
|
||||||
|
|
||||||
### Postgresql
|
### Database backend
|
||||||
|
|
||||||
|
#### Postgresql
|
||||||
|
|
||||||
```py
|
```py
|
||||||
pip install ormar[postgresql]
|
pip install ormar[postgresql]
|
||||||
```
|
```
|
||||||
Will install also `asyncpg` and `psycopg2`.
|
Will install also `asyncpg` and `psycopg2`.
|
||||||
|
|
||||||
### Mysql
|
#### Mysql
|
||||||
|
|
||||||
```py
|
```py
|
||||||
pip install ormar[mysql]
|
pip install ormar[mysql]
|
||||||
@ -36,7 +39,7 @@ pip install ormar[mysql]
|
|||||||
|
|
||||||
Will install also `aiomysql` and `pymysql`.
|
Will install also `aiomysql` and `pymysql`.
|
||||||
|
|
||||||
### Sqlite
|
#### Sqlite
|
||||||
|
|
||||||
```py
|
```py
|
||||||
pip install ormar[sqlite]
|
pip install ormar[sqlite]
|
||||||
|
|||||||
330
docs/migration.md
Normal file
330
docs/migration.md
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
# Migration to 0.20.0 based on pydantic 2.X.X
|
||||||
|
|
||||||
|
Version 0.20.0 provides support for pydantic v2.X.X that provides significant speed boost (validation and serialization is written in rust) and cleaner api for developers,
|
||||||
|
at the same time it drops support for pydantic v.1.X.X. There are changes in `ormar` interface corresponding to changes made in `pydantic`.
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
Migration to version >= 0.20.0 requires several changes in order to work properly.
|
||||||
|
|
||||||
|
## `ormar` Model configuration
|
||||||
|
|
||||||
|
Instead of defining a `Meta` class now each of the ormar models require an ormar_config parameter that is an instance of the `OrmarConfig` class.
|
||||||
|
Note that the attribute must be named `ormar_config` and be an instance of the config class.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
# ormar < 0.20
|
||||||
|
class Album(ormar.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database
|
||||||
|
metadata = metadata
|
||||||
|
tablename = "albums"
|
||||||
|
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
favorite: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
class AlbumV20(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="albums_v20"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
favorite: bool = ormar.Boolean(default=False)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `OrmarConfig` api/ parameters
|
||||||
|
|
||||||
|
The `ormar_config` expose the same set of settings as `Meta` class used to provide.
|
||||||
|
That means that you can use any of the following parameters initializing the config:
|
||||||
|
|
||||||
|
```python
|
||||||
|
metadata: Optional[sqlalchemy.MetaData]
|
||||||
|
database: Optional[databases.Database]
|
||||||
|
engine: Optional[sqlalchemy.engine.Engine]
|
||||||
|
tablename: Optional[str]
|
||||||
|
order_by: Optional[List[str]]
|
||||||
|
abstract: bool
|
||||||
|
exclude_parent_fields: Optional[List[str]]
|
||||||
|
queryset_class: Type[QuerySet]
|
||||||
|
extra: Extra
|
||||||
|
constraints: Optional[List[ColumnCollectionConstraint]]
|
||||||
|
```
|
||||||
|
|
||||||
|
### `BaseMeta` equivalent - best practice
|
||||||
|
|
||||||
|
Note that to reduce the duplication of code and ease of development it's still recommended to create a base config and provide each of the models with a copy.
|
||||||
|
OrmarConfig provides a convenient `copy` method for that purpose.
|
||||||
|
|
||||||
|
The `copy` method accepts the same parameters as `OrmarConfig` init, so you can overwrite if needed, but by default it will return already existing attributes, except for: `tablename`, `order_by` and `constraints` which by default are cleared.
|
||||||
|
|
||||||
|
```python hl_lines="5-8 11 20"
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database("sqlite:///db.sqlite"),
|
||||||
|
metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
class AlbumV20(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(
|
||||||
|
tablename="albums_v20"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
|
class TrackV20(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(
|
||||||
|
tablename="tracks_v20"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
```
|
||||||
|
|
||||||
|
## `choices` Field parameter is no longer supported.
|
||||||
|
|
||||||
|
Before version 0.20 you could provide `choices` parameter to any existing ormar Field to limit the accepted values.
|
||||||
|
This functionality was dropped, and you should use `ormar.Enum` field that was designed for this purpose.
|
||||||
|
If you want to keep the database field type (i.e. an Integer field) you can always write a custom validator.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
# ormar < 0.20
|
||||||
|
class Artist(ormar.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database
|
||||||
|
metadata = metadata
|
||||||
|
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
country: str = ormar.String(default=False, max_length=50, choices=["UK", "US", "Vietnam", "Colombia"])
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class Country(str, Enum):
|
||||||
|
UK = "UK"
|
||||||
|
US = "US"
|
||||||
|
VIETNAM = "Vietnam"
|
||||||
|
COLOMBIA = "Colombia"
|
||||||
|
|
||||||
|
class ArtistV20(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="artists_v20"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
country: Country = ormar.Enum(enum_class=Country)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `pydantic_only` Field parameter is no longer supported
|
||||||
|
|
||||||
|
`pydantic_only` fields were already deprecated and are removed in v 0.20. Ormar allows defining pydantic fields as in ordinary pydantic model.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
# ormar < 0.20
|
||||||
|
class Dish(ormar.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database
|
||||||
|
metadata = metadata
|
||||||
|
tablename = "dishes"
|
||||||
|
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
cook: str = ormar.String(max_length=40, pydantic_only=True, default="sam")
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
class DishV20(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="dishes_v20"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
cook: str = "sam" # this is normal pydantic field
|
||||||
|
```
|
||||||
|
|
||||||
|
## `property_field` decorator is no longer supported
|
||||||
|
|
||||||
|
`property_field` decorator was used to provide a way to pass calculated fields that were included in dictionary/ serialized json representation of the model.
|
||||||
|
Version 2.X of pydantic introduced such a possibility, so you should now switch to the one native to the pydantic.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
import pydantic
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
# ormar < 0.20
|
||||||
|
class Employee(ormar.Model):
|
||||||
|
class Meta:
|
||||||
|
database = database
|
||||||
|
metadata = metadata
|
||||||
|
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
first_name: str = ormar.String(max_length=100)
|
||||||
|
last_name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
@ormar.property_field()
|
||||||
|
def full_name(self) -> str:
|
||||||
|
return f"{self.first_name} {self.last_name}"
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
class EmployeeV20(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
first_name: str = ormar.String(max_length=100)
|
||||||
|
last_name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
@pydantic.computed_field()
|
||||||
|
def full_name(self) -> str:
|
||||||
|
return f"{self.first_name} {self.last_name}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deprecated methods
|
||||||
|
|
||||||
|
All methods listed below are deprecated and will be removed in version 0.30 of `ormar`.
|
||||||
|
|
||||||
|
### `dict()` becomes the `model_dump()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
class Album(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="albums"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
favorite: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
album = Album(name="Dark Side of the Moon")
|
||||||
|
|
||||||
|
# ormar < 0.20
|
||||||
|
album_dict = album.dict()
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
new_album_dict = album.model_dump()
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that parameters remain the same i.e. `include`, `exclude` etc.
|
||||||
|
|
||||||
|
### `json()` becomes the `model_dump_json()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
class Album(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="albums"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
favorite: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
album = Album(name="Dark Side of the Moon")
|
||||||
|
|
||||||
|
# ormar < 0.20
|
||||||
|
album_json= album.json()
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
new_album_dict = album.model_dump_json()
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that parameters remain the same i.e. `include`, `exclude` etc.
|
||||||
|
|
||||||
|
### `construct()` becomes the `model_construct()`
|
||||||
|
|
||||||
|
```python
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
class Album(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="albums"
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
favorite: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"name": "Dark Side of the Moon",
|
||||||
|
"favorite": True,
|
||||||
|
}
|
||||||
|
# ormar < 0.20
|
||||||
|
album = Album.construct(**params)
|
||||||
|
|
||||||
|
# ormar >= 0.20
|
||||||
|
album = Album.model_construct(**params)
|
||||||
|
```
|
||||||
|
|
||||||
|
To read more about construct please refer to `pydantic` documentation.
|
||||||
@ -9,7 +9,7 @@ They are being managed in the background and you do not have to create them on y
|
|||||||
|
|
||||||
To build an ormar model you simply need to inherit a `ormar.Model` class.
|
To build an ormar model you simply need to inherit a `ormar.Model` class.
|
||||||
|
|
||||||
```Python hl_lines="10"
|
```Python hl_lines="9"
|
||||||
--8<-- "../docs_src/models/docs001.py"
|
--8<-- "../docs_src/models/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ Each table **has to** have a primary key column, which you specify by setting `p
|
|||||||
|
|
||||||
Only one primary key column is allowed.
|
Only one primary key column is allowed.
|
||||||
|
|
||||||
```Python hl_lines="15 16 17"
|
```Python hl_lines="15-17"
|
||||||
--8<-- "../docs_src/models/docs001.py"
|
--8<-- "../docs_src/models/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -42,15 +42,15 @@ id: int = ormar.Integer(primary_key=True, autoincrement=False)
|
|||||||
#### Non Database Fields
|
#### Non Database Fields
|
||||||
|
|
||||||
Note that if you need a normal pydantic field in your model (used to store value on model or pass around some value) you can define a
|
Note that if you need a normal pydantic field in your model (used to store value on model or pass around some value) you can define a
|
||||||
field with parameter `pydantic_only=True`.
|
field like usual in pydantic.
|
||||||
|
|
||||||
Fields created like this are added to the `pydantic` model fields -> so are subject to validation according to `Field` type,
|
Fields created like this are added to the `pydantic` model fields -> so are subject to validation according to `Field` type,
|
||||||
also appear in `dict()` and `json()` result.
|
also appear in `model_dump()` and `model_dump_json()` result.
|
||||||
|
|
||||||
The difference is that **those fields are not saved in the database**. So they won't be included in underlying sqlalchemy `columns`,
|
The difference is that **those fields are not saved in the database**. So they won't be included in underlying sqlalchemy `columns`,
|
||||||
or `table` variables (check [Internals][Internals] section below to see how you can access those if you need).
|
or `table` variables (check [Internals][Internals] section below to see how you can access those if you need).
|
||||||
|
|
||||||
Subsequently `pydantic_only` fields won't be included in migrations or any database operation (like `save`, `update` etc.)
|
Subsequently, pydantic fields won't be included in migrations or any database operation (like `save`, `update` etc.)
|
||||||
|
|
||||||
Fields like those can be passed around into payload in `fastapi` request and will be returned in `fastapi` response
|
Fields like those can be passed around into payload in `fastapi` request and will be returned in `fastapi` response
|
||||||
(of course only if you set their value somewhere in your code as the value is **not** fetched from the db.
|
(of course only if you set their value somewhere in your code as the value is **not** fetched from the db.
|
||||||
@ -58,30 +58,32 @@ If you pass a value in `fastapi` `request` and return the same instance that `fa
|
|||||||
you should get back exactly same value in `response`.).
|
you should get back exactly same value in `response`.).
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
`pydantic_only=True` fields are always **Optional** and it cannot be changed (otherwise db load validation would fail)
|
pydantic fields have to be always **Optional** and it cannot be changed (otherwise db load validation would fail)
|
||||||
|
|
||||||
!!!tip
|
```Python hl_lines="19"
|
||||||
`pydantic_only=True` fields are a good solution if you need to pass additional information from outside of your API
|
|
||||||
(i.e. frontend). They are not stored in db but you can access them in your `APIRoute` code and they also have `pydantic` validation.
|
|
||||||
|
|
||||||
```Python hl_lines="18"
|
|
||||||
--8<-- "../docs_src/models/docs014.py"
|
--8<-- "../docs_src/models/docs014.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
If you combine `pydantic_only=True` field with `default` parameter and do not pass actual value in request you will always get default value.
|
If you set pydantic field with `default` parameter and do not pass actual value in request you will always get default value.
|
||||||
Since it can be a function you can set `default=datetime.datetime.now` and get current timestamp each time you call an endpoint etc.
|
Since it can be a function you can set `default=datetime.datetime.now` and get current timestamp each time you call an endpoint etc.
|
||||||
|
|
||||||
|
#### Non Database Fields in Fastapi
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
Note that both `pydantic_only` and `property_field` decorated field can be included/excluded in both `dict()` and `fastapi`
|
Note, that both pydantic and calculated_fields decorated field can be included/excluded in both `model_dump()` and `fastapi`
|
||||||
response with `include`/`exclude` and `response_model_include`/`response_model_exclude` accordingly.
|
response with `include`/`exclude` and `response_model_include`/`response_model_exclude` accordingly.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# <==related of code removed for clarity==>
|
# <==part of related code removed for clarity==>
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
engine=sqlalchemy.create_engine(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class User(ormar.Model):
|
class User(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="users2")
|
||||||
tablename: str = "users2"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
email: str = ormar.String(max_length=255, nullable=False)
|
email: str = ormar.String(max_length=255, nullable=False)
|
||||||
@ -89,18 +91,18 @@ class User(ormar.Model):
|
|||||||
first_name: str = ormar.String(max_length=255)
|
first_name: str = ormar.String(max_length=255)
|
||||||
last_name: str = ormar.String(max_length=255)
|
last_name: str = ormar.String(max_length=255)
|
||||||
category: str = ormar.String(max_length=255, nullable=True)
|
category: str = ormar.String(max_length=255, nullable=True)
|
||||||
timestamp: datetime.datetime = ormar.DateTime(
|
timestamp: datetime.datetime = pydantic.Field(
|
||||||
pydantic_only=True, default=datetime.datetime.now
|
default=datetime.datetime.now
|
||||||
)
|
)
|
||||||
|
|
||||||
# <==related of code removed for clarity==>
|
# <==part of related code removed for clarity==>
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
@app.post("/users/")
|
@app.post("/users/")
|
||||||
async def create_user(user: User):
|
async def create_user(user: User):
|
||||||
return await user.save()
|
return await user.save()
|
||||||
|
|
||||||
# <==related of code removed for clarity==>
|
# <==part of related code removed for clarity==>
|
||||||
|
|
||||||
def test_excluding_fields_in_endpoints():
|
def test_excluding_fields_in_endpoints():
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
@ -127,121 +129,7 @@ def test_excluding_fields_in_endpoints():
|
|||||||
assert response.json().get("timestamp") == str(timestamp).replace(" ", "T")
|
assert response.json().get("timestamp") == str(timestamp).replace(" ", "T")
|
||||||
|
|
||||||
|
|
||||||
# <==related of code removed for clarity==>
|
# <==part of related code removed for clarity==>
|
||||||
```
|
|
||||||
|
|
||||||
#### Property fields
|
|
||||||
|
|
||||||
Sometimes it's desirable to do some kind of calculation on the model instance. One of the most common examples can be concatenating
|
|
||||||
two or more fields. Imagine you have `first_name` and `last_name` fields on your model, but would like to have `full_name` in the result
|
|
||||||
of the `fastapi` query.
|
|
||||||
|
|
||||||
You can create a new `pydantic` model with a `method` that accepts only `self` (so like default python `@property`)
|
|
||||||
and populate it in your code.
|
|
||||||
|
|
||||||
But it's so common that `ormar` has you covered. You can "materialize" a `property_field` on you `Model`.
|
|
||||||
|
|
||||||
!!!warning
|
|
||||||
`property_field` fields are always **Optional** and it cannot be changed (otherwise db load validation would fail)
|
|
||||||
|
|
||||||
```Python hl_lines="20-22"
|
|
||||||
--8<-- "../docs_src/models/docs015.py"
|
|
||||||
```
|
|
||||||
|
|
||||||
!!!warning
|
|
||||||
The decorated function has to accept only one parameter, and that parameter have to be `self`.
|
|
||||||
|
|
||||||
If you try to decorate a function with more parameters `ormar` will raise `ModelDefinitionError`.
|
|
||||||
|
|
||||||
Sample:
|
|
||||||
|
|
||||||
```python
|
|
||||||
# will raise ModelDefinitionError
|
|
||||||
@property_field
|
|
||||||
def prefixed_name(self, prefix="prefix_"):
|
|
||||||
return 'custom_prefix__' + self.name
|
|
||||||
|
|
||||||
# will raise ModelDefinitionError
|
|
||||||
# (calling first param something else than 'self' is a bad practice anyway)
|
|
||||||
@property_field
|
|
||||||
def prefixed_name(instance):
|
|
||||||
return 'custom_prefix__' + self.name
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that `property_field` decorated methods do not go through verification (but that might change in future) and are only available
|
|
||||||
in the response from `fastapi` and `dict()` and `json()` methods. You cannot pass a value for this field in the request
|
|
||||||
(or rather you can but it will be discarded by ormar so really no point but no Exception will be raised).
|
|
||||||
|
|
||||||
!!!note
|
|
||||||
Note that both `pydantic_only` and `property_field` decorated field can be included/excluded in both `dict()` and `fastapi`
|
|
||||||
response with `include`/`exclude` and `response_model_include`/`response_model_exclude` accordingly.
|
|
||||||
|
|
||||||
!!!tip
|
|
||||||
Note that `@property_field` decorator is designed to replace the python `@property` decorator, you do not have to combine them.
|
|
||||||
|
|
||||||
In theory you can cause `ormar` have a failsafe mechanism, but note that i.e. `mypy` will complain about re-decorating a property.
|
|
||||||
|
|
||||||
```python
|
|
||||||
# valid and working but unnecessary and mypy will complain
|
|
||||||
@property_field
|
|
||||||
@property
|
|
||||||
def prefixed_name(self):
|
|
||||||
return 'custom_prefix__' + self.name
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
# <==related of code removed for clarity==>
|
|
||||||
def gen_pass(): # note: NOT production ready
|
|
||||||
choices = string.ascii_letters + string.digits + "!@#$%^&*()"
|
|
||||||
return "".join(random.choice(choices) for _ in range(20))
|
|
||||||
|
|
||||||
class RandomModel(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename: str = "random_users"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
include_props_in_dict = True
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
|
||||||
password: str = ormar.String(max_length=255, default=gen_pass)
|
|
||||||
first_name: str = ormar.String(max_length=255, default="John")
|
|
||||||
last_name: str = ormar.String(max_length=255)
|
|
||||||
created_date: datetime.datetime = ormar.DateTime(
|
|
||||||
server_default=sqlalchemy.func.now()
|
|
||||||
)
|
|
||||||
|
|
||||||
@property_field
|
|
||||||
def full_name(self) -> str:
|
|
||||||
return " ".join([self.first_name, self.last_name])
|
|
||||||
|
|
||||||
# <==related of code removed for clarity==>
|
|
||||||
app =FastAPI()
|
|
||||||
|
|
||||||
# explicitly exclude property_field in this endpoint
|
|
||||||
@app.post("/random/", response_model=RandomModel, response_model_exclude={"full_name"})
|
|
||||||
async def create_user(user: RandomModel):
|
|
||||||
return await user.save()
|
|
||||||
|
|
||||||
# <==related of code removed for clarity==>
|
|
||||||
|
|
||||||
def test_excluding_property_field_in_endpoints2():
|
|
||||||
client = TestClient(app)
|
|
||||||
with client as client:
|
|
||||||
RandomModel.Meta.include_props_in_dict = True
|
|
||||||
user3 = {"last_name": "Test"}
|
|
||||||
response = client.post("/random3/", json=user3)
|
|
||||||
assert list(response.json().keys()) == [
|
|
||||||
"id",
|
|
||||||
"password",
|
|
||||||
"first_name",
|
|
||||||
"last_name",
|
|
||||||
"created_date",
|
|
||||||
]
|
|
||||||
# despite being decorated with property_field if you explicitly exclude it it will be gone
|
|
||||||
assert response.json().get("full_name") is None
|
|
||||||
|
|
||||||
# <==related of code removed for clarity==>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Fields names vs Column names
|
#### Fields names vs Column names
|
||||||
@ -252,25 +140,25 @@ If for whatever reason you prefer to change the name in the database but keep th
|
|||||||
with specifying `name` parameter during Field declaration
|
with specifying `name` parameter during Field declaration
|
||||||
|
|
||||||
Here you have a sample model with changed names
|
Here you have a sample model with changed names
|
||||||
```Python hl_lines="16-19"
|
```Python hl_lines="18-21"
|
||||||
--8<-- "../docs_src/models/docs008.py"
|
--8<-- "../docs_src/models/docs008.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you can also change the ForeignKey column name
|
Note that you can also change the ForeignKey column name
|
||||||
```Python hl_lines="21"
|
```Python hl_lines="34"
|
||||||
--8<-- "../docs_src/models/docs009.py"
|
--8<-- "../docs_src/models/docs009.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
But for now you cannot change the ManyToMany column names as they go through other Model anyway.
|
But for now you cannot change the ManyToMany column names as they go through other Model anyway.
|
||||||
```Python hl_lines="28"
|
```Python hl_lines="43"
|
||||||
--8<-- "../docs_src/models/docs010.py"
|
--8<-- "../docs_src/models/docs010.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Overwriting the default QuerySet
|
### Overwriting the default QuerySet
|
||||||
|
|
||||||
If you want to customize the queries run by ormar you can define your own queryset class (that extends the ormar `QuerySet`) in your model class, default one is simply the `QuerySet`
|
If you want to customize the queries run by ormar you can define your own queryset class (that extends the ormar `QuerySet`) in your model class, default one is simply the `QuerySet`
|
||||||
|
|
||||||
You can provide a new class in `Meta` configuration of your class as `queryset_class` parameter.
|
You can provide a new class in `ormar_config` of your class as `queryset_class` parameter.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import ormar
|
import ormar
|
||||||
@ -288,12 +176,10 @@ class MyQuerySetClass(QuerySet):
|
|||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(
|
||||||
class Meta(ormar.ModelMeta):
|
queryset_class=MyQuerySetClass,
|
||||||
metadata = metadata
|
tablename="book",
|
||||||
database = database
|
)
|
||||||
tablename = "book"
|
|
||||||
queryset_class = MyQuerySetClass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=32)
|
name: str = ormar.String(max_length=32)
|
||||||
@ -304,17 +190,9 @@ book = await Book.objects.first_or_404(name="123")
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Type Hints & Legacy
|
### Type Hints
|
||||||
|
|
||||||
Before version 0.4.0 `ormar` supported only one way of defining `Fields` on a `Model` using python type hints as pydantic.
|
Note that for better IDE support and mypy checks you can provide type hints.
|
||||||
|
|
||||||
```Python hl_lines="15-17"
|
|
||||||
--8<-- "../docs_src/models/docs011.py"
|
|
||||||
```
|
|
||||||
|
|
||||||
But that didn't play well with static type checkers like `mypy` and `pydantic` PyCharm plugin.
|
|
||||||
|
|
||||||
Therefore from version >=0.4.0 `ormar` switched to new notation.
|
|
||||||
|
|
||||||
```Python hl_lines="15-17"
|
```Python hl_lines="15-17"
|
||||||
--8<-- "../docs_src/models/docs001.py"
|
--8<-- "../docs_src/models/docs001.py"
|
||||||
@ -343,9 +221,9 @@ and table creation you need to assign each `Model` with two special parameters.
|
|||||||
|
|
||||||
One is `Database` instance created with your database url in [sqlalchemy connection string][sqlalchemy connection string] format.
|
One is `Database` instance created with your database url in [sqlalchemy connection string][sqlalchemy connection string] format.
|
||||||
|
|
||||||
Created instance needs to be passed to every `Model` with `Meta` class `database` parameter.
|
Created instance needs to be passed to every `Model` with `ormar_config` object `database` parameter.
|
||||||
|
|
||||||
```Python hl_lines="1 6 12"
|
```Python hl_lines="1 5 11"
|
||||||
--8<-- "../docs_src/models/docs001.py"
|
--8<-- "../docs_src/models/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -357,9 +235,9 @@ Created instance needs to be passed to every `Model` with `Meta` class `database
|
|||||||
|
|
||||||
Second dependency is sqlalchemy `MetaData` instance.
|
Second dependency is sqlalchemy `MetaData` instance.
|
||||||
|
|
||||||
Created instance needs to be passed to every `Model` with `Meta` class `metadata` parameter.
|
Created instance needs to be passed to every `Model` with `ormar_config` object `metadata` parameter.
|
||||||
|
|
||||||
```Python hl_lines="2 7 13"
|
```Python hl_lines="3 6 12"
|
||||||
--8<-- "../docs_src/models/docs001.py"
|
--8<-- "../docs_src/models/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -369,25 +247,22 @@ Created instance needs to be passed to every `Model` with `Meta` class `metadata
|
|||||||
|
|
||||||
#### Best practice
|
#### Best practice
|
||||||
|
|
||||||
Only thing that `ormar` expects is a class with name `Meta` and two class variables: `metadata` and `databases`.
|
Note that `ormar` expects the field with name `ormar_config` that is an instance of `OrmarConfig` class.
|
||||||
|
To ease the config management, the `OrmarConfig` class provide `copy` method.
|
||||||
|
So instead of providing the same parameters over and over again for all models
|
||||||
|
you should create a base object and use its copy in all models.
|
||||||
|
|
||||||
So instead of providing the same parameters over and over again for all models you should creata a class and subclass it in all models.
|
```Python hl_lines="9-12 19 28"
|
||||||
|
|
||||||
```Python hl_lines="14 20 33"
|
|
||||||
--8<-- "../docs_src/models/docs013.py"
|
--8<-- "../docs_src/models/docs013.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!warning
|
|
||||||
You need to subclass your `MainMeta` class in each `Model` class as those classes store configuration variables
|
|
||||||
that otherwise would be overwritten by each `Model`.
|
|
||||||
|
|
||||||
### Table Names
|
### Table Names
|
||||||
|
|
||||||
By default table name is created from Model class name as lowercase name plus 's'.
|
By default table name is created from Model class name as lowercase name plus 's'.
|
||||||
|
|
||||||
You can overwrite this parameter by providing `Meta` class `tablename` argument.
|
You can overwrite this parameter by providing `ormar_config` object's `tablename` argument.
|
||||||
|
|
||||||
```Python hl_lines="12 13 14"
|
```Python hl_lines="14-16"
|
||||||
--8<-- "../docs_src/models/docs002.py"
|
--8<-- "../docs_src/models/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -395,7 +270,7 @@ You can overwrite this parameter by providing `Meta` class `tablename` argument.
|
|||||||
|
|
||||||
On a model level you can also set model-wise constraints on sql columns.
|
On a model level you can also set model-wise constraints on sql columns.
|
||||||
|
|
||||||
Right now only `IndexColumns` and `UniqueColumns` constraints are supported.
|
Right now only `IndexColumns`, `UniqueColumns` and `CheckColumns` constraints are supported.
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
Note that both constraints should be used only if you want to set a name on constraint or want to set the index on multiple columns, otherwise `index` and `unique` properties on ormar fields are preferred.
|
Note that both constraints should be used only if you want to set a name on constraint or want to set the index on multiple columns, otherwise `index` and `unique` properties on ormar fields are preferred.
|
||||||
@ -405,9 +280,9 @@ Right now only `IndexColumns` and `UniqueColumns` constraints are supported.
|
|||||||
|
|
||||||
#### UniqueColumns
|
#### UniqueColumns
|
||||||
|
|
||||||
You can set this parameter by providing `Meta` class `constraints` argument.
|
You can set this parameter by providing `ormar_config` object `constraints` argument.
|
||||||
|
|
||||||
```Python hl_lines="14-17"
|
```Python hl_lines="13-16"
|
||||||
--8<-- "../docs_src/models/docs006.py"
|
--8<-- "../docs_src/models/docs006.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -418,9 +293,9 @@ You can set this parameter by providing `Meta` class `constraints` argument.
|
|||||||
|
|
||||||
#### IndexColumns
|
#### IndexColumns
|
||||||
|
|
||||||
You can set this parameter by providing `Meta` class `constraints` argument.
|
You can set this parameter by providing `ormar_config` object `constraints` argument.
|
||||||
|
|
||||||
```Python hl_lines="14-17"
|
```Python hl_lines="13-16"
|
||||||
--8<-- "../docs_src/models/docs017.py"
|
--8<-- "../docs_src/models/docs017.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -431,23 +306,23 @@ You can set this parameter by providing `Meta` class `constraints` argument.
|
|||||||
|
|
||||||
#### CheckColumns
|
#### CheckColumns
|
||||||
|
|
||||||
You can set this parameter by providing `Meta` class `constraints` argument.
|
You can set this parameter by providing `ormar_config` object `constraints` argument.
|
||||||
|
|
||||||
```Python hl_lines="14-17"
|
```Python hl_lines="15-20"
|
||||||
--8<-- "../docs_src/models/docs018.py"
|
--8<-- "../docs_src/models/docs018.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
Note that some databases do not actively support check constraints such as MySQL.
|
Note that some databases do not actively support check constraints (such as MySQL).
|
||||||
|
|
||||||
|
|
||||||
### Pydantic configuration
|
### Pydantic configuration
|
||||||
|
|
||||||
As each `ormar.Model` is also a `pydantic` model, you might want to tweak the settings of the pydantic configuration.
|
As each `ormar.Model` is also a `pydantic` model, you might want to tweak the settings of the pydantic configuration.
|
||||||
|
|
||||||
The way to do this in pydantic is to adjust the settings on the `Config` class provided to your model, and it works exactly the same for ormar models.
|
The way to do this in pydantic is to adjust the settings on the `model_config` dictionary provided to your model, and it works exactly the same for ormar models.
|
||||||
|
|
||||||
So in order to set your own preferences you need to provide not only the `Meta` class but also the `Config` class to your model.
|
So in order to set your own preferences you need to provide not only the `ormar_config` class but also the `model_config = ConfigDict()` class to your model.
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
To read more about available settings visit the [pydantic](https://pydantic-docs.helpmanual.io/usage/model_config/) config page.
|
To read more about available settings visit the [pydantic](https://pydantic-docs.helpmanual.io/usage/model_config/) config page.
|
||||||
@ -456,13 +331,11 @@ Note that if you do not provide your own configuration, ormar will do it for you
|
|||||||
The default config provided is as follows:
|
The default config provided is as follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Config(pydantic.BaseConfig):
|
model_config = ConfigDict(validate_assignment=True, ser_json_bytes="base64")
|
||||||
orm_mode = True
|
|
||||||
validate_assignment = True
|
|
||||||
```
|
```
|
||||||
|
|
||||||
So to overwrite setting or provide your own a sample model can look like following:
|
So to overwrite setting or provide your own a sample model can look like following:
|
||||||
```Python hl_lines="15-16"
|
```Python hl_lines="16"
|
||||||
--8<-- "../docs_src/models/docs016.py"
|
--8<-- "../docs_src/models/docs016.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -474,69 +347,64 @@ If you try to do so the `ModelError` will be raised.
|
|||||||
|
|
||||||
Since the extra fields cannot be saved in the database the default to disallow such fields seems a feasible option.
|
Since the extra fields cannot be saved in the database the default to disallow such fields seems a feasible option.
|
||||||
|
|
||||||
On the contrary in `pydantic` the default option is to ignore such extra fields, therefore `ormar` provides an `Meta.extra` setting to behave in the same way.
|
On the contrary in `pydantic` the default option is to ignore such extra fields, therefore `ormar` provides an `ormar_config.extra` setting to behave in the same way.
|
||||||
|
|
||||||
To ignore extra fields passed to `ormar` set this setting to `Extra.ignore` instead of default `Extra.forbid`.
|
To ignore extra fields passed to `ormar` set this setting to `Extra.ignore` instead of default `Extra.forbid`.
|
||||||
|
|
||||||
Note that `ormar` does not allow accepting extra fields, you can only ignore them or forbid them (raise exception if present)
|
Note that `ormar` does not allow accepting extra fields, you can only ignore them or forbid them (raise exception if present)
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from ormar import Extra
|
from ormar import Extra, OrmarConfig
|
||||||
|
|
||||||
class Child(ormar.Model):
|
class Child(ormar.Model):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = OrmarConfig(
|
||||||
tablename = "children"
|
tablename="children",
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
extra=Extra.ignore # set extra setting to prevent exceptions on extra fields presence
|
extra=Extra.ignore # set extra setting to prevent exceptions on extra fields presence
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(name="child_id", primary_key=True)
|
id: int = ormar.Integer(name="child_id", primary_key=True)
|
||||||
first_name: str = ormar.String(name="fname", max_length=100)
|
first_name: str = ormar.String(name="fname", max_length=100)
|
||||||
last_name: str = ormar.String(name="lname", max_length=100)
|
last_name: str = ormar.String(name="lname", max_length=100)
|
||||||
```
|
```
|
||||||
|
|
||||||
To set the same setting on all model check the [best practices]("../models/index/#best-practice") and `BaseMeta` concept.
|
To set the same setting on all model check the [best practices]("../models/index/#best-practice") and `base_ormar_config` concept.
|
||||||
|
|
||||||
## Model sort order
|
## Model sort order
|
||||||
|
|
||||||
When querying the database with given model by default the Model is ordered by the `primary_key`
|
When querying the database with given model by default the Model is ordered by the `primary_key`
|
||||||
column ascending. If you wish to change the default behaviour you can do it by providing `orders_by`
|
column ascending. If you wish to change the default behaviour you can do it by providing `orders_by`
|
||||||
parameter to model `Meta` class.
|
parameter to model `ormar_config` object.
|
||||||
|
|
||||||
Sample default ordering:
|
Sample default ordering (not specified - so by primary key):
|
||||||
```python
|
```python
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
# default sort by column id ascending
|
# default sort by column id ascending
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(
|
||||||
tablename = "authors"
|
tablename="authors",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
```
|
```
|
||||||
Modified
|
Modified
|
||||||
```python
|
```python hl_lines="9"
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
database = databases.Database(DATABASE_URL)
|
database=databases.Database(DATABASE_URL),
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
# now default sort by name descending
|
# now default sort by name descending
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(
|
||||||
tablename = "authors"
|
orders_by = ["-name"],
|
||||||
orders_by = ["-name"]
|
tablename="authors",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -546,12 +414,9 @@ class Author(ormar.Model):
|
|||||||
|
|
||||||
There are two ways to create and persist the `Model` instance in the database.
|
There are two ways to create and persist the `Model` instance in the database.
|
||||||
|
|
||||||
!!!tip
|
|
||||||
Use `ipython` to try this from the console, since it supports `await`.
|
|
||||||
|
|
||||||
If you plan to modify the instance in the later execution of your program you can initiate your `Model` as a normal class and later await a `save()` call.
|
If you plan to modify the instance in the later execution of your program you can initiate your `Model` as a normal class and later await a `save()` call.
|
||||||
|
|
||||||
```Python hl_lines="20 21"
|
```Python hl_lines="25-26"
|
||||||
--8<-- "../docs_src/models/docs007.py"
|
--8<-- "../docs_src/models/docs007.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -561,7 +426,7 @@ For creating multiple objects at once a `bulk_create()` QuerySet's method is ava
|
|||||||
|
|
||||||
Each model has a `QuerySet` initialised as `objects` parameter
|
Each model has a `QuerySet` initialised as `objects` parameter
|
||||||
|
|
||||||
```Python hl_lines="23"
|
```Python hl_lines="28"
|
||||||
--8<-- "../docs_src/models/docs007.py"
|
--8<-- "../docs_src/models/docs007.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ Out of various types of ORM models inheritance `ormar` currently supports two of
|
|||||||
|
|
||||||
## Types of inheritance
|
## Types of inheritance
|
||||||
|
|
||||||
The short summary of different types of inheritance is:
|
The short summary of different types of inheritance:
|
||||||
|
|
||||||
* **Mixins [SUPPORTED]** - don't subclass `ormar.Model`, just define fields that are
|
* **Mixins [SUPPORTED]** - don't subclass `ormar.Model`, just define fields that are
|
||||||
later used on different models (like `created_date` and `updated_date` on each model),
|
later used on different models (like `created_date` and `updated_date` on each model),
|
||||||
@ -32,6 +32,13 @@ To use Mixins just define a class that is not inheriting from an `ormar.Model` b
|
|||||||
defining `ormar.Fields` as class variables.
|
defining `ormar.Fields` as class variables.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
engine=sqlalchemy.create_engine(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# a mixin defines the fields but is a normal python class
|
# a mixin defines the fields but is a normal python class
|
||||||
class AuditMixin:
|
class AuditMixin:
|
||||||
created_by: str = ormar.String(max_length=100)
|
created_by: str = ormar.String(max_length=100)
|
||||||
@ -45,10 +52,7 @@ class DateFieldsMixins:
|
|||||||
|
|
||||||
# a models can inherit from one or more mixins
|
# a models can inherit from one or more mixins
|
||||||
class Category(ormar.Model, DateFieldsMixins, AuditMixin):
|
class Category(ormar.Model, DateFieldsMixins, AuditMixin):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=50, unique=True, index=True)
|
name: str = ormar.String(max_length=50, unique=True, index=True)
|
||||||
@ -57,7 +61,7 @@ class Category(ormar.Model, DateFieldsMixins, AuditMixin):
|
|||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
Note that Mixins are **not** models, so you still need to inherit
|
Note that Mixins are **not** models, so you still need to inherit
|
||||||
from `ormar.Model` as well as define `Meta` class in the **final** model.
|
from `ormar.Model` as well as define `ormar_config` field in the **final** model.
|
||||||
|
|
||||||
A Category class above will have four additional fields: `created_date`, `updated_date`,
|
A Category class above will have four additional fields: `created_date`, `updated_date`,
|
||||||
`created_by` and `updated_by`.
|
`created_by` and `updated_by`.
|
||||||
@ -73,11 +77,11 @@ In concept concrete table inheritance is very similar to Mixins, but uses
|
|||||||
actual `ormar.Models` as base classes.
|
actual `ormar.Models` as base classes.
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
Note that base classes have `abstract=True` set in `Meta` class, if you try
|
Note that base classes have `abstract=True` set in `ormar_config` object, if you try
|
||||||
to inherit from non abstract marked class `ModelDefinitionError` will be raised.
|
to inherit from non abstract marked class `ModelDefinitionError` will be raised.
|
||||||
|
|
||||||
Since this abstract Model will never be initialized you can skip `metadata`
|
Since this abstract Model will never be initialized you can skip `metadata`
|
||||||
and `database` in it's `Meta` definition.
|
and `database` in it's `ormar_config` definition.
|
||||||
|
|
||||||
But if you provide it - it will be inherited, that way you do not have to
|
But if you provide it - it will be inherited, that way you do not have to
|
||||||
provide `metadata` and `databases` in the final/concrete class
|
provide `metadata` and `databases` in the final/concrete class
|
||||||
@ -91,8 +95,7 @@ otherwise an error will be raised.
|
|||||||
# note that base classes have abstract=True
|
# note that base classes have abstract=True
|
||||||
# since this model will never be initialized you can skip metadata and database
|
# since this model will never be initialized you can skip metadata and database
|
||||||
class AuditModel(ormar.Model):
|
class AuditModel(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
|
|
||||||
created_by: str = ormar.String(max_length=100)
|
created_by: str = ormar.String(max_length=100)
|
||||||
updated_by: str = ormar.String(max_length=100, default="Sam")
|
updated_by: str = ormar.String(max_length=100, default="Sam")
|
||||||
@ -100,10 +103,11 @@ class AuditModel(ormar.Model):
|
|||||||
|
|
||||||
# but if you provide it it will be inherited - DRY (Don't Repeat Yourself) in action
|
# but if you provide it it will be inherited - DRY (Don't Repeat Yourself) in action
|
||||||
class DateFieldsModel(ormar.Model):
|
class DateFieldsModel(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(
|
||||||
abstract = True
|
abstract=True,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = db
|
database=db,
|
||||||
|
)
|
||||||
|
|
||||||
created_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)
|
created_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)
|
||||||
updated_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)
|
updated_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)
|
||||||
@ -111,8 +115,7 @@ class DateFieldsModel(ormar.Model):
|
|||||||
|
|
||||||
# that way you do not have to provide metadata and databases in concrete class
|
# that way you do not have to provide metadata and databases in concrete class
|
||||||
class Category(DateFieldsModel, AuditModel):
|
class Category(DateFieldsModel, AuditModel):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=50, unique=True, index=True)
|
name: str = ormar.String(max_length=50, unique=True, index=True)
|
||||||
@ -123,8 +126,6 @@ class Category(DateFieldsModel, AuditModel):
|
|||||||
The list of inherited options/settings is as follows: `metadata`, `database`
|
The list of inherited options/settings is as follows: `metadata`, `database`
|
||||||
and `constraints`.
|
and `constraints`.
|
||||||
|
|
||||||
Also methods decorated with `@property_field` decorator will be inherited/recognized.
|
|
||||||
|
|
||||||
Of course apart from that all fields from base classes are combined and created in the
|
Of course apart from that all fields from base classes are combined and created in the
|
||||||
concrete table of the final Model.
|
concrete table of the final Model.
|
||||||
|
|
||||||
@ -140,15 +141,16 @@ inheritance.
|
|||||||
Whenever you define a field with same name and new definition it will completely replace
|
Whenever you define a field with same name and new definition it will completely replace
|
||||||
the previously defined one.
|
the previously defined one.
|
||||||
|
|
||||||
```python
|
```python hl_lines="28"
|
||||||
# base class
|
# base class
|
||||||
class DateFieldsModel(ormar.Model):
|
class DateFieldsModel(ormar.Model):
|
||||||
class Meta:
|
ormar_config = OrmarConfig(
|
||||||
abstract = True
|
abstract=True,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = db
|
database=db,
|
||||||
# note that UniqueColumns need sqlalchemy db columns names not the ormar ones
|
# note that UniqueColumns need sqlalchemy db columns names not the ormar ones
|
||||||
constraints=[ormar.UniqueColumns("creation_date", "modification_date")]
|
constraints=[ormar.UniqueColumns("creation_date", "modification_date")]
|
||||||
|
)
|
||||||
|
|
||||||
created_date: datetime.datetime = ormar.DateTime(
|
created_date: datetime.datetime = ormar.DateTime(
|
||||||
default=datetime.datetime.now, name="creation_date"
|
default=datetime.datetime.now, name="creation_date"
|
||||||
@ -159,10 +161,11 @@ class DateFieldsModel(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class RedefinedField(DateFieldsModel):
|
class RedefinedField(DateFieldsModel):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = OrmarConfig(
|
||||||
tablename = "redefines"
|
tablename="redefines",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = db
|
database=db,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
# here the created_date is replaced by the String field
|
# here the created_date is replaced by the String field
|
||||||
@ -170,12 +173,12 @@ class RedefinedField(DateFieldsModel):
|
|||||||
|
|
||||||
|
|
||||||
# you can verify that the final field is correctly declared and created
|
# you can verify that the final field is correctly declared and created
|
||||||
changed_field = RedefinedField.Meta.model_fields["created_date"]
|
changed_field = RedefinedField.ormar_config.model_fields["created_date"]
|
||||||
assert changed_field.default is None
|
assert changed_field.default is None
|
||||||
assert changed_field.alias == "creation_date"
|
assert changed_field.alias == "creation_date"
|
||||||
assert any(x.name == "creation_date" for x in RedefinedField.Meta.table.columns)
|
assert any(x.name == "creation_date" for x in RedefinedField.ormar_config.table.columns)
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
RedefinedField.Meta.table.columns["creation_date"].type,
|
RedefinedField.ormar_config.table.columns["creation_date"].type,
|
||||||
sqlalchemy.sql.sqltypes.String,
|
sqlalchemy.sql.sqltypes.String,
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -225,9 +228,7 @@ That might sound complicated but let's look at the following example:
|
|||||||
```python
|
```python
|
||||||
# normal model used in relation
|
# normal model used in relation
|
||||||
class Person(ormar.Model):
|
class Person(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -235,10 +236,7 @@ class Person(ormar.Model):
|
|||||||
|
|
||||||
# parent model - needs to be abstract
|
# parent model - needs to be abstract
|
||||||
class Car(ormar.Model):
|
class Car(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=50)
|
name: str = ormar.String(max_length=50)
|
||||||
@ -249,16 +247,13 @@ class Car(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Truck(Car):
|
class Truck(Car):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
max_capacity: int = ormar.Integer()
|
max_capacity: int = ormar.Integer()
|
||||||
|
|
||||||
|
|
||||||
class Bus(Car):
|
class Bus(Car):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="buses")
|
||||||
# default naming is name.lower()+'s' so it's ugly for buss ;)
|
|
||||||
tablename = "buses"
|
|
||||||
|
|
||||||
max_persons: int = ormar.Integer()
|
max_persons: int = ormar.Integer()
|
||||||
```
|
```
|
||||||
@ -266,7 +261,7 @@ class Bus(Car):
|
|||||||
Now when you will inspect the fields on Person model you will get:
|
Now when you will inspect the fields on Person model you will get:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Person.Meta.model_fields
|
Person.ormar_config.model_fields
|
||||||
"""
|
"""
|
||||||
{'id': <class 'ormar.fields.model_fields.Integer'>,
|
{'id': <class 'ormar.fields.model_fields.Integer'>,
|
||||||
'name': <class 'ormar.fields.model_fields.String'>,
|
'name': <class 'ormar.fields.model_fields.String'>,
|
||||||
@ -293,8 +288,7 @@ different `related_name` parameter.
|
|||||||
```python
|
```python
|
||||||
# rest of the above example remains the same
|
# rest of the above example remains the same
|
||||||
class Bus(Car):
|
class Bus(Car):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="buses")
|
||||||
tablename = "buses"
|
|
||||||
|
|
||||||
# new field that changes the related_name
|
# new field that changes the related_name
|
||||||
owner: Person = ormar.ForeignKey(Person, related_name="buses")
|
owner: Person = ormar.ForeignKey(Person, related_name="buses")
|
||||||
@ -304,7 +298,7 @@ class Bus(Car):
|
|||||||
Now the columns looks much better.
|
Now the columns looks much better.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Person.Meta.model_fields
|
Person.ormar_config.model_fields
|
||||||
"""
|
"""
|
||||||
{'id': <class 'ormar.fields.model_fields.Integer'>,
|
{'id': <class 'ormar.fields.model_fields.Integer'>,
|
||||||
'name': <class 'ormar.fields.model_fields.String'>,
|
'name': <class 'ormar.fields.model_fields.String'>,
|
||||||
@ -328,7 +322,7 @@ Person.Meta.model_fields
|
|||||||
Similarly, you can inherit from Models that have ManyToMany relations declared but
|
Similarly, you can inherit from Models that have ManyToMany relations declared but
|
||||||
there is one, but substantial difference - the Through model.
|
there is one, but substantial difference - the Through model.
|
||||||
|
|
||||||
Since in the future the Through model will be able to hold additional fields and now it links only two Tables
|
Since the Through model will be able to hold additional fields, and now it links only two Tables
|
||||||
(`from` and `to` ones), each child that inherits the m2m relation field has to have separate
|
(`from` and `to` ones), each child that inherits the m2m relation field has to have separate
|
||||||
Through model.
|
Through model.
|
||||||
|
|
||||||
@ -344,27 +338,18 @@ We will modify the previous example described above to use m2m relation for co_o
|
|||||||
```python
|
```python
|
||||||
# person remain the same as above
|
# person remain the same as above
|
||||||
class Person(ormar.Model):
|
class Person(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
# new through model between Person and Car2
|
# new through model between Person and Car2
|
||||||
class PersonsCar(ormar.Model):
|
class PersonsCar(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="cars_x_persons")
|
||||||
tablename = "cars_x_persons"
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
# note how co_owners is now ManyToMany relation
|
# note how co_owners is now ManyToMany relation
|
||||||
class Car2(ormar.Model):
|
class Car2(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
# parent class needs to be marked abstract
|
|
||||||
abstract = True
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=50)
|
name: str = ormar.String(max_length=50)
|
||||||
@ -379,16 +364,13 @@ class Car2(ormar.Model):
|
|||||||
|
|
||||||
# child models define only additional Fields
|
# child models define only additional Fields
|
||||||
class Truck2(Car2):
|
class Truck2(Car2):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="trucks2")
|
||||||
# note how you don't have to provide inherited Meta params
|
|
||||||
tablename = "trucks2"
|
|
||||||
|
|
||||||
max_capacity: int = ormar.Integer()
|
max_capacity: int = ormar.Integer()
|
||||||
|
|
||||||
|
|
||||||
class Bus2(Car2):
|
class Bus2(Car2):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="buses2")
|
||||||
tablename = "buses2"
|
|
||||||
|
|
||||||
max_persons: int = ormar.Integer()
|
max_persons: int = ormar.Integer()
|
||||||
```
|
```
|
||||||
@ -402,7 +384,7 @@ That way for class Truck2 the relation defined in
|
|||||||
You can verify the names by inspecting the list of fields present on `Person` model.
|
You can verify the names by inspecting the list of fields present on `Person` model.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Person.Meta.model_fields
|
Person.ormar_config.model_fields
|
||||||
{
|
{
|
||||||
# note how all relation fields need to be unique on Person
|
# note how all relation fields need to be unique on Person
|
||||||
# regardless if autogenerated or manually overwritten
|
# regardless if autogenerated or manually overwritten
|
||||||
@ -425,14 +407,14 @@ But that's not all. It's kind of internal to `ormar` but affects the data struct
|
|||||||
so let's examine the through models for both `Bus2` and `Truck2` models.
|
so let's examine the through models for both `Bus2` and `Truck2` models.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Bus2.Meta.model_fields['co_owners'].through
|
Bus2.ormar_config.model_fields['co_owners'].through
|
||||||
<class 'abc.PersonsCarBus2'>
|
<class 'abc.PersonsCarBus2'>
|
||||||
Bus2.Meta.model_fields['co_owners'].through.Meta.tablename
|
Bus2.ormar_config.model_fields['co_owners'].through.ormar_config.tablename
|
||||||
'cars_x_persons_buses2'
|
'cars_x_persons_buses2'
|
||||||
|
|
||||||
Truck2.Meta.model_fields['co_owners'].through
|
Truck2.ormar_config.model_fields['co_owners'].through
|
||||||
<class 'abc.PersonsCarTruck2'>
|
<class 'abc.PersonsCarTruck2'>
|
||||||
Truck2.Meta.model_fields['co_owners'].through.Meta.tablename
|
Truck2.ormar_config.model_fields['co_owners'].through.ormar_config.tablename
|
||||||
'cars_x_persons_trucks2'
|
'cars_x_persons_trucks2'
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -443,7 +425,7 @@ the name of the **table** from the child is used.
|
|||||||
Note that original model is not only not used, the table for this model is removed from metadata:
|
Note that original model is not only not used, the table for this model is removed from metadata:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
Bus2.Meta.metadata.tables.keys()
|
Bus2.ormar_config.metadata.tables.keys()
|
||||||
dict_keys(['test_date_models', 'categories', 'subjects', 'persons', 'trucks', 'buses',
|
dict_keys(['test_date_models', 'categories', 'subjects', 'persons', 'trucks', 'buses',
|
||||||
'cars_x_persons_trucks2', 'trucks2', 'cars_x_persons_buses2', 'buses2'])
|
'cars_x_persons_trucks2', 'trucks2', 'cars_x_persons_buses2', 'buses2'])
|
||||||
```
|
```
|
||||||
@ -469,26 +451,24 @@ Ormar allows you to skip certain fields in inherited model that are coming from
|
|||||||
!!!Note
|
!!!Note
|
||||||
Note that the same behaviour can be achieved by splitting the model into more abstract models and mixins - which is a preferred way in normal circumstances.
|
Note that the same behaviour can be achieved by splitting the model into more abstract models and mixins - which is a preferred way in normal circumstances.
|
||||||
|
|
||||||
To skip certain fields from a child model, list all fields that you want to skip in `model.Meta.exclude_parent_fields` parameter like follows:
|
To skip certain fields from a child model, list all fields that you want to skip in `model.ormar_config.exclude_parent_fields` parameter like follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
metadata = sa.MetaData()
|
base_ormar_config = OrmarConfig(
|
||||||
db = databases.Database(DATABASE_URL)
|
metadata=sa.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuditModel(ormar.Model):
|
class AuditModel(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
|
|
||||||
created_by: str = ormar.String(max_length=100)
|
created_by: str = ormar.String(max_length=100)
|
||||||
updated_by: str = ormar.String(max_length=100, default="Sam")
|
updated_by: str = ormar.String(max_length=100, default="Sam")
|
||||||
|
|
||||||
|
|
||||||
class DateFieldsModel(ormar.Model):
|
class DateFieldsModel(ormar.Model):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
created_date: datetime.datetime = ormar.DateTime(
|
created_date: datetime.datetime = ormar.DateTime(
|
||||||
default=datetime.datetime.now, name="creation_date"
|
default=datetime.datetime.now, name="creation_date"
|
||||||
@ -499,10 +479,11 @@ class DateFieldsModel(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Category(DateFieldsModel, AuditModel):
|
class Category(DateFieldsModel, AuditModel):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(
|
||||||
tablename = "categories"
|
tablename="categories",
|
||||||
# set fields that should be skipped
|
# set fields that should be skipped
|
||||||
exclude_parent_fields = ["updated_by", "updated_date"]
|
exclude_parent_fields=["updated_by", "updated_date"],
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=50, unique=True, index=True)
|
name: str = ormar.String(max_length=50, unique=True, index=True)
|
||||||
@ -523,38 +504,32 @@ Note how you simply need to provide field names and it will exclude the parent f
|
|||||||
The same effect can be achieved by splitting base classes like:
|
The same effect can be achieved by splitting base classes like:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
metadata = sa.MetaData()
|
base_ormar_config = OrmarConfig(
|
||||||
db = databases.Database(DATABASE_URL)
|
metadata=sa.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuditCreateModel(ormar.Model):
|
class AuditCreateModel(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
|
|
||||||
created_by: str = ormar.String(max_length=100)
|
created_by: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class AuditUpdateModel(ormar.Model):
|
class AuditUpdateModel(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
|
|
||||||
updated_by: str = ormar.String(max_length=100, default="Sam")
|
updated_by: str = ormar.String(max_length=100, default="Sam")
|
||||||
|
|
||||||
class CreateDateFieldsModel(ormar.Model):
|
class CreateDateFieldsModel(ormar.Model):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
created_date: datetime.datetime = ormar.DateTime(
|
created_date: datetime.datetime = ormar.DateTime(
|
||||||
default=datetime.datetime.now, name="creation_date"
|
default=datetime.datetime.now, name="creation_date"
|
||||||
)
|
)
|
||||||
|
|
||||||
class UpdateDateFieldsModel(ormar.Model):
|
class UpdateDateFieldsModel(ormar.Model):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(abstract=True)
|
||||||
abstract = True
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
updated_date: datetime.datetime = ormar.DateTime(
|
updated_date: datetime.datetime = ormar.DateTime(
|
||||||
default=datetime.datetime.now, name="modification_date"
|
default=datetime.datetime.now, name="modification_date"
|
||||||
@ -562,8 +537,7 @@ class UpdateDateFieldsModel(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Category(CreateDateFieldsModel, AuditCreateModel):
|
class Category(CreateDateFieldsModel, AuditCreateModel):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=50, unique=True, index=True)
|
name: str = ormar.String(max_length=50, unique=True, index=True)
|
||||||
|
|||||||
@ -20,27 +20,27 @@ For example to list pydantic model fields you can:
|
|||||||
|
|
||||||
## Sqlalchemy Table
|
## Sqlalchemy Table
|
||||||
|
|
||||||
To access auto created sqlalchemy table you can use `Model.Meta.table` parameter
|
To access auto created sqlalchemy table you can use `Model.ormar_config.table` parameter
|
||||||
|
|
||||||
For example to list table columns you can:
|
For example to list table columns you can:
|
||||||
|
|
||||||
```Python hl_lines="20"
|
```Python hl_lines="24"
|
||||||
--8<-- "../docs_src/models/docs004.py"
|
--8<-- "../docs_src/models/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
You can access table primary key name by `Course.Meta.pkname`
|
You can access table primary key name by `Course.ormar_config.pkname`
|
||||||
|
|
||||||
!!!info
|
!!!info
|
||||||
For more options visit official [sqlalchemy-metadata][sqlalchemy-metadata] documentation.
|
For more options visit official [sqlalchemy-metadata][sqlalchemy-metadata] documentation.
|
||||||
|
|
||||||
## Fields Definition
|
## Fields Definition
|
||||||
|
|
||||||
To access ormar `Fields` you can use `Model.Meta.model_fields` parameter
|
To access ormar `Fields` you can use `Model.ormar_config.model_fields` parameter
|
||||||
|
|
||||||
For example to list table model fields you can:
|
For example to list table model fields you can:
|
||||||
|
|
||||||
```Python hl_lines="20"
|
```Python hl_lines="22"
|
||||||
--8<-- "../docs_src/models/docs005.py"
|
--8<-- "../docs_src/models/docs005.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -13,19 +13,19 @@ Available methods are described below.
|
|||||||
## `pydantic` methods
|
## `pydantic` methods
|
||||||
|
|
||||||
Note that each `ormar.Model` is also a `pydantic.BaseModel`, so all `pydantic` methods are also available on a model,
|
Note that each `ormar.Model` is also a `pydantic.BaseModel`, so all `pydantic` methods are also available on a model,
|
||||||
especially `dict()` and `json()` methods that can also accept `exclude`, `include` and other parameters.
|
especially `model_dump()` and `model_dump_json()` methods that can also accept `exclude`, `include` and other parameters.
|
||||||
|
|
||||||
To read more check [pydantic][pydantic] documentation
|
To read more check [pydantic][pydantic] documentation
|
||||||
|
|
||||||
## construct
|
## model_construct()
|
||||||
|
|
||||||
`construct` is a raw equivalent of `__init__` method used for construction of new instances.
|
`model_construct` is a raw equivalent of `__init__` method used for construction of new instances.
|
||||||
|
|
||||||
The difference is that `construct` skips validations, so it should be used when you know that data is correct and can be trusted.
|
The difference is that `model_construct` skips validations, so it should be used when you know that data is correct and can be trusted.
|
||||||
The benefit of using construct is the speed of execution due to skipped validation.
|
The benefit of using construct is the speed of execution due to skipped validation.
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
Note that in contrast to `pydantic.construct` method - the `ormar` equivalent will also process the nested related models.
|
Note that in contrast to `pydantic.model_construct` method - the `ormar` equivalent will also process the nested related models.
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
Bear in mind that due to skipped validation the `construct` method does not perform any conversions, checks etc.
|
Bear in mind that due to skipped validation the `construct` method does not perform any conversions, checks etc.
|
||||||
@ -36,14 +36,14 @@ The benefit of using construct is the speed of execution due to skipped validati
|
|||||||
* Providing a `default` value for not set fields
|
* Providing a `default` value for not set fields
|
||||||
* Initialize nested ormar models if you pass a dictionary or a primary key value
|
* Initialize nested ormar models if you pass a dictionary or a primary key value
|
||||||
|
|
||||||
## dict
|
## model_dump()
|
||||||
|
|
||||||
`dict` is a method inherited from `pydantic`, yet `ormar` adds its own parameters and has some nuances when working with default values,
|
`model_dump` is a method inherited from `pydantic`, yet `ormar` adds its own parameters and has some nuances when working with default values,
|
||||||
therefore it's listed here for clarity.
|
therefore it's listed here for clarity.
|
||||||
|
|
||||||
`dict` as the name suggests export data from model tree to dictionary.
|
`model_dump` as the name suggests export data from model tree to dictionary.
|
||||||
|
|
||||||
Explanation of dict parameters:
|
Explanation of model_dump parameters:
|
||||||
|
|
||||||
### include (`ormar` modified)
|
### include (`ormar` modified)
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ Note that `pydantic` has an uncommon pattern of including/ excluding fields in l
|
|||||||
And if you want to exclude the field in all children you need to pass a `__all__` key to dictionary.
|
And if you want to exclude the field in all children you need to pass a `__all__` key to dictionary.
|
||||||
|
|
||||||
You cannot exclude nested models in `Set`s in `pydantic` but you can in `ormar`
|
You cannot exclude nested models in `Set`s in `pydantic` but you can in `ormar`
|
||||||
(by adding double underscore on relation name i.e. to exclude name of category for a book you cen use `exclude={"book__category__name"}`)
|
(by adding double underscore on relation name i.e. to exclude name of category for a book you can use `exclude={"book__category__name"}`)
|
||||||
|
|
||||||
`ormar` does not support by index exclusion/ inclusions and accepts a simplified and more user-friendly notation.
|
`ormar` does not support by index exclusion/ inclusions and accepts a simplified and more user-friendly notation.
|
||||||
|
|
||||||
@ -96,10 +96,7 @@ Flag indicates whether fields which were not explicitly set when creating the mo
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, default="Test")
|
name: str = ormar.String(max_length=100, default="Test")
|
||||||
@ -107,10 +104,7 @@ class Category(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -118,17 +112,17 @@ class Item(ormar.Model):
|
|||||||
categories: List[Category] = ormar.ManyToMany(Category)
|
categories: List[Category] = ormar.ManyToMany(Category)
|
||||||
|
|
||||||
category = Category(name="Test 2")
|
category = Category(name="Test 2")
|
||||||
assert category.dict() == {'id': None, 'items': [], 'name': 'Test 2',
|
assert category.model_dump() == {'id': None, 'items': [], 'name': 'Test 2',
|
||||||
'visibility': True}
|
'visibility': True}
|
||||||
assert category.dict(exclude_unset=True) == {'items': [], 'name': 'Test 2'}
|
assert category.model_dump(exclude_unset=True) == {'items': [], 'name': 'Test 2'}
|
||||||
|
|
||||||
await category.save()
|
await category.save()
|
||||||
category2 = await Category.objects.get()
|
category2 = await Category.objects.get()
|
||||||
assert category2.dict() == {'id': 1, 'items': [], 'name': 'Test 2',
|
assert category2.model_dump() == {'id': 1, 'items': [], 'name': 'Test 2',
|
||||||
'visibility': True}
|
'visibility': True}
|
||||||
# NOTE how after loading from db all fields are set explicitly
|
# NOTE how after loading from db all fields are set explicitly
|
||||||
# as this is what happens when you populate a model from db
|
# as this is what happens when you populate a model from db
|
||||||
assert category2.dict(exclude_unset=True) == {'id': 1, 'items': [],
|
assert category2.model_dump(exclude_unset=True) == {'id': 1, 'items': [],
|
||||||
'name': 'Test 2', 'visibility': True}
|
'name': 'Test 2', 'visibility': True}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -140,20 +134,14 @@ Flag indicates are equal to their default values (whether set or otherwise) shou
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, default="Test")
|
name: str = ormar.String(max_length=100, default="Test")
|
||||||
visibility: bool = ormar.Boolean(default=True)
|
visibility: bool = ormar.Boolean(default=True)
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -162,15 +150,15 @@ class Item(ormar.Model):
|
|||||||
|
|
||||||
category = Category()
|
category = Category()
|
||||||
# note that Integer pk is by default autoincrement so optional
|
# note that Integer pk is by default autoincrement so optional
|
||||||
assert category.dict() == {'id': None, 'items': [], 'name': 'Test', 'visibility': True}
|
assert category.model_dump() == {'id': None, 'items': [], 'name': 'Test', 'visibility': True}
|
||||||
assert category.dict(exclude_defaults=True) == {'items': []}
|
assert category.model_dump(exclude_defaults=True) == {'items': []}
|
||||||
|
|
||||||
# save and reload the data
|
# save and reload the data
|
||||||
await category.save()
|
await category.save()
|
||||||
category2 = await Category.objects.get()
|
category2 = await Category.objects.get()
|
||||||
|
|
||||||
assert category2.dict() == {'id': 1, 'items': [], 'name': 'Test', 'visibility': True}
|
assert category2.model_dump() == {'id': 1, 'items': [], 'name': 'Test', 'visibility': True}
|
||||||
assert category2.dict(exclude_defaults=True) == {'id': 1, 'items': []}
|
assert category2.model_dump(exclude_defaults=True) == {'id': 1, 'items': []}
|
||||||
```
|
```
|
||||||
|
|
||||||
### exclude_none
|
### exclude_none
|
||||||
@ -181,10 +169,7 @@ Flag indicates whether fields which are equal to `None` should be excluded from
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, default="Test", nullable=True)
|
name: str = ormar.String(max_length=100, default="Test", nullable=True)
|
||||||
@ -192,10 +177,7 @@ class Category(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -204,16 +186,16 @@ class Item(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
category = Category(name=None)
|
category = Category(name=None)
|
||||||
assert category.dict() == {'id': None, 'items': [], 'name': None,
|
assert category.model_dump() == {'id': None, 'items': [], 'name': None,
|
||||||
'visibility': True}
|
'visibility': True}
|
||||||
# note the id is not set yet so None and excluded
|
# note the id is not set yet so None and excluded
|
||||||
assert category.dict(exclude_none=True) == {'items': [], 'visibility': True}
|
assert category.model_dump(exclude_none=True) == {'items': [], 'visibility': True}
|
||||||
|
|
||||||
await category.save()
|
await category.save()
|
||||||
category2 = await Category.objects.get()
|
category2 = await Category.objects.get()
|
||||||
assert category2.dict() == {'id': 1, 'items': [], 'name': None,
|
assert category2.model_dump() == {'id': 1, 'items': [], 'name': None,
|
||||||
'visibility': True}
|
'visibility': True}
|
||||||
assert category2.dict(exclude_none=True) == {'id': 1, 'items': [],
|
assert category2.model_dump(exclude_none=True) == {'id': 1, 'items': [],
|
||||||
'visibility': True}
|
'visibility': True}
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -226,17 +208,14 @@ Setting flag to `True` will exclude all primary key columns in a tree, including
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
item1 = Item(id=1, name="Test Item")
|
item1 = Item(id=1, name="Test Item")
|
||||||
assert item1.dict() == {"id": 1, "name": "Test Item"}
|
assert item1.model_dump() == {"id": 1, "name": "Test Item"}
|
||||||
assert item1.dict(exclude_primary_keys=True) == {"name": "Test Item"}
|
assert item1.model_dump(exclude_primary_keys=True) == {"name": "Test Item"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### exclude_through_models (`ormar` only)
|
### exclude_through_models (`ormar` only)
|
||||||
@ -249,20 +228,14 @@ Setting the `exclude_through_models=True` will exclude all through models, inclu
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -280,7 +253,7 @@ await Item(**item_dict).save_related(follow=True, save_all=True)
|
|||||||
item = await Item.objects.select_related("categories").get()
|
item = await Item.objects.select_related("categories").get()
|
||||||
|
|
||||||
# by default you can see the through models (itemcategory)
|
# by default you can see the through models (itemcategory)
|
||||||
assert item.dict() == {'id': 1, 'name': 'test',
|
assert item.model_dump() == {'id': 1, 'name': 'test',
|
||||||
'categories': [
|
'categories': [
|
||||||
{'id': 1, 'name': 'test cat',
|
{'id': 1, 'name': 'test cat',
|
||||||
'itemcategory': {'id': 1, 'category': None, 'item': None}},
|
'itemcategory': {'id': 1, 'category': None, 'item': None}},
|
||||||
@ -289,7 +262,7 @@ assert item.dict() == {'id': 1, 'name': 'test',
|
|||||||
]}
|
]}
|
||||||
|
|
||||||
# you can exclude those fields/ models
|
# you can exclude those fields/ models
|
||||||
assert item.dict(exclude_through_models=True) == {
|
assert item.model_dump(exclude_through_models=True) == {
|
||||||
'id': 1, 'name': 'test',
|
'id': 1, 'name': 'test',
|
||||||
'categories': [
|
'categories': [
|
||||||
{'id': 1, 'name': 'test cat'},
|
{'id': 1, 'name': 'test cat'},
|
||||||
@ -297,19 +270,19 @@ assert item.dict(exclude_through_models=True) == {
|
|||||||
]}
|
]}
|
||||||
```
|
```
|
||||||
|
|
||||||
## json
|
## model_dump_json()
|
||||||
|
|
||||||
`json()` has exactly the same parameters as `dict()` so check above.
|
`model_dump_json()` has exactly the same parameters as `model_dump()` so check above.
|
||||||
|
|
||||||
Of course the end result is a string with json representation and not a dictionary.
|
Of course the end result is a string with json representation and not a dictionary.
|
||||||
|
|
||||||
## get_pydantic
|
## get_pydantic()
|
||||||
|
|
||||||
`get_pydantic(include: Union[Set, Dict] = None, exclude: Union[Set, Dict] = None)`
|
`get_pydantic(include: Union[Set, Dict] = None, exclude: Union[Set, Dict] = None)`
|
||||||
|
|
||||||
This method allows you to generate `pydantic` models from your ormar models without you needing to retype all the fields.
|
This method allows you to generate `pydantic` models from your ormar models without you needing to retype all the fields.
|
||||||
|
|
||||||
Note that if you have nested models, it **will generate whole tree of pydantic models for you!**
|
Note that if you have nested models, it **will generate whole tree of pydantic models for you!** but in a way that prevents cyclic references issues.
|
||||||
|
|
||||||
Moreover, you can pass `exclude` and/or `include` parameters to keep only the fields that you want to, including in nested models.
|
Moreover, you can pass `exclude` and/or `include` parameters to keep only the fields that you want to, including in nested models.
|
||||||
|
|
||||||
@ -321,25 +294,21 @@ That means that this way you can effortlessly create pydantic models for request
|
|||||||
Given sample ormar models like follows:
|
Given sample ormar models like follows:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
metadata = sqlalchemy.MetaData()
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database=databases.Database(DATABASE_URL, force_rollback=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, default="test")
|
name: str = ormar.String(max_length=100, default="test")
|
||||||
@ -382,7 +351,7 @@ class Category(BaseModel):
|
|||||||
items: Optional[List[Item]]
|
items: Optional[List[Item]]
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, you can use also deeply nested structures and ormar will generate it pydantic equivalent you (in a way that exclude loops).
|
Of course, you can use also deeply nested structures and ormar will generate it's pydantic equivalent for you (in a way that exclude loops).
|
||||||
|
|
||||||
Note how `Item` model above does not have a reference to `Category` although in ormar the relation is bidirectional (and `ormar.Item` has `categories` field).
|
Note how `Item` model above does not have a reference to `Category` although in ormar the relation is bidirectional (and `ormar.Item` has `categories` field).
|
||||||
|
|
||||||
@ -392,9 +361,9 @@ Note how `Item` model above does not have a reference to `Category` although in
|
|||||||
But, at the same time all root validators present on `ormar` models will **NOT** be copied to the generated pydantic model. Since root validator can operate on all fields and a user can exclude some fields during generation of pydantic model it's not safe to copy those validators.
|
But, at the same time all root validators present on `ormar` models will **NOT** be copied to the generated pydantic model. Since root validator can operate on all fields and a user can exclude some fields during generation of pydantic model it's not safe to copy those validators.
|
||||||
If required, you need to redefine/ manually copy them to generated pydantic model.
|
If required, you need to redefine/ manually copy them to generated pydantic model.
|
||||||
|
|
||||||
## load
|
## load()
|
||||||
|
|
||||||
By default when you query a table without prefetching related models, the ormar will still construct
|
By default, when you query a table without prefetching related models, the ormar will still construct
|
||||||
your related models, but populate them only with the pk value. You can load the related model by calling `load()` method.
|
your related models, but populate them only with the pk value. You can load the related model by calling `load()` method.
|
||||||
|
|
||||||
`load()` can also be used to refresh the model from the database (if it was changed by some other process).
|
`load()` can also be used to refresh the model from the database (if it was changed by some other process).
|
||||||
@ -409,14 +378,14 @@ await track.album.load()
|
|||||||
track.album.name # will return 'Malibu'
|
track.album.name # will return 'Malibu'
|
||||||
```
|
```
|
||||||
|
|
||||||
## load_all
|
## load_all()
|
||||||
|
|
||||||
`load_all(follow: bool = False, exclude: Union[List, str, Set, Dict] = None) -> Model`
|
`load_all(follow: bool = False, exclude: Union[List, str, Set, Dict] = None) -> Model`
|
||||||
|
|
||||||
Method works like `load()` but also goes through all relations of the `Model` on which the method is called,
|
Method works like `load()` but also goes through all relations of the `Model` on which the method is called,
|
||||||
and reloads them from database.
|
and reloads them from database.
|
||||||
|
|
||||||
By default the `load_all` method loads only models that are directly related (one step away) to the model on which the method is called.
|
By default, the `load_all` method loads only models that are directly related (one step away) to the model on which the method is called.
|
||||||
|
|
||||||
But you can specify the `follow=True` parameter to traverse through nested models and load all of them in the relation tree.
|
But you can specify the `follow=True` parameter to traverse through nested models and load all of them in the relation tree.
|
||||||
|
|
||||||
@ -442,7 +411,7 @@ Method performs one database query so it's more efficient than nested calls to `
|
|||||||
!!!warning
|
!!!warning
|
||||||
All relations are cleared on `load_all()`, so if you exclude some nested models they will be empty after call.
|
All relations are cleared on `load_all()`, so if you exclude some nested models they will be empty after call.
|
||||||
|
|
||||||
## save
|
## save()
|
||||||
|
|
||||||
`save() -> self`
|
`save() -> self`
|
||||||
|
|
||||||
@ -461,7 +430,7 @@ track = await Track.objects.get(name='The Bird')
|
|||||||
await track.save() # will raise integrity error as pk is populated
|
await track.save() # will raise integrity error as pk is populated
|
||||||
```
|
```
|
||||||
|
|
||||||
## update
|
## update()
|
||||||
|
|
||||||
`update(_columns: List[str] = None, **kwargs) -> self`
|
`update(_columns: List[str] = None, **kwargs) -> self`
|
||||||
|
|
||||||
@ -480,12 +449,9 @@ To update only selected columns from model into the database provide a list of c
|
|||||||
|
|
||||||
In example:
|
In example:
|
||||||
|
|
||||||
```python
|
```python hl_lines="16"
|
||||||
class Movie(ormar.Model):
|
class Movie(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "movies"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, nullable=False, name="title")
|
name: str = ormar.String(max_length=100, nullable=False, name="title")
|
||||||
@ -512,7 +478,7 @@ assert terminator.year == 1984
|
|||||||
!!!warning
|
!!!warning
|
||||||
Note that `update()` does not refresh the instance of the Model, so if you change more columns than you pass in `_columns` list your Model instance will have different values than the database!
|
Note that `update()` does not refresh the instance of the Model, so if you change more columns than you pass in `_columns` list your Model instance will have different values than the database!
|
||||||
|
|
||||||
## upsert
|
## upsert()
|
||||||
|
|
||||||
`upsert(**kwargs) -> self`
|
`upsert(**kwargs) -> self`
|
||||||
|
|
||||||
@ -531,7 +497,7 @@ await track.upsert(name='The Bird Strikes Again') # will call update as pk is al
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## delete
|
## delete()
|
||||||
|
|
||||||
You can delete models by using `QuerySet.delete()` method or by using your model and calling `delete()` method.
|
You can delete models by using `QuerySet.delete()` method or by using your model and calling `delete()` method.
|
||||||
|
|
||||||
@ -543,7 +509,7 @@ await track.delete() # will delete the model from database
|
|||||||
!!!tip
|
!!!tip
|
||||||
Note that that `track` object stays the same, only record in the database is removed.
|
Note that that `track` object stays the same, only record in the database is removed.
|
||||||
|
|
||||||
## save_related
|
## save_related()
|
||||||
|
|
||||||
`save_related(follow: bool = False, save_all: bool = False, exclude=Optional[Union[Set, Dict]]) -> None`
|
`save_related(follow: bool = False, save_all: bool = False, exclude=Optional[Union[Set, Dict]]) -> None`
|
||||||
|
|
||||||
@ -584,18 +550,14 @@ Example:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
department_name: str = ormar.String(max_length=100)
|
department_name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
course_name: str = ormar.String(max_length=100)
|
course_name: str = ormar.String(max_length=100)
|
||||||
@ -604,9 +566,7 @@ class Course(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Student(ormar.Model):
|
class Student(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -647,7 +607,7 @@ to_exclude = {
|
|||||||
}
|
}
|
||||||
# after excluding ids and through models you get exact same payload used to
|
# after excluding ids and through models you get exact same payload used to
|
||||||
# construct whole tree
|
# construct whole tree
|
||||||
assert department_check.dict(exclude=to_exclude) == to_save
|
assert department_check.model_dump(exclude=to_exclude) == to_save
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -16,14 +16,14 @@ engine = sqlalchemy.create_engine("sqlite:///test.db")
|
|||||||
metadata.create_all(engine)
|
metadata.create_all(engine)
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also create single tables, sqlalchemy tables are exposed in `ormar.Meta` class.
|
You can also create single tables, sqlalchemy tables are exposed in `ormar.ormar_config` object.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
# get your database url in sqlalchemy format - same as used with databases instance used in Model definition
|
# get your database url in sqlalchemy format - same as used with databases instance used in Model definition
|
||||||
engine = sqlalchemy.create_engine("sqlite:///test.db")
|
engine = sqlalchemy.create_engine("sqlite:///test.db")
|
||||||
# Artist is an ormar model from previous examples
|
# Artist is an ormar model from previous examples
|
||||||
Artist.Meta.table.create(engine)
|
Artist.ormar_config.table.create(engine)
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
|
|||||||
10
docs/mypy.md
10
docs/mypy.md
@ -1,14 +1,6 @@
|
|||||||
To provide better errors check you should use mypy with pydantic [plugin][plugin]
|
To provide better errors check you should use mypy with pydantic [plugin][plugin]
|
||||||
|
|
||||||
Note that legacy model declaration type will raise static type analyzers errors.
|
Please use notation introduced in version 0.4.0.
|
||||||
|
|
||||||
So you **cannot use the old notation** like this:
|
|
||||||
|
|
||||||
```Python hl_lines="15-17"
|
|
||||||
--8<-- "../docs_src/models/docs011.py"
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead switch to notation introduced in version 0.4.0.
|
|
||||||
|
|
||||||
```Python hl_lines="15-17"
|
```Python hl_lines="15-17"
|
||||||
--8<-- "../docs_src/models/docs012.py"
|
--8<-- "../docs_src/models/docs012.py"
|
||||||
|
|||||||
@ -32,10 +32,11 @@ the count will be the total number of rows returned
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "books"
|
database=databases.Database(DATABASE_URL),
|
||||||
metadata = metadata
|
metadata=sqlalchemy.MetaData(),
|
||||||
database = database
|
tablename="book"
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
@ -60,10 +61,11 @@ Returns a bool value to confirm if there are rows matching the given criteria (a
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "books"
|
database=databases.Database(DATABASE_URL),
|
||||||
metadata = metadata
|
metadata=sqlalchemy.MetaData(),
|
||||||
database = database
|
tablename="book"
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
|
|||||||
@ -30,10 +30,12 @@ The allowed kwargs are `Model` fields names and proper value types.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
tablename="album"
|
tablename="album"
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -68,10 +70,11 @@ i.e. `get_or_create(_defaults: {"title": "I win"}, title="never used")` will alw
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
tablename="album"
|
tablename="album"
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -106,7 +109,7 @@ assert album == album2
|
|||||||
|
|
||||||
Updates the model, or in case there is no match in database creates a new one.
|
Updates the model, or in case there is no match in database creates a new one.
|
||||||
|
|
||||||
```Python hl_lines="26-32"
|
```Python hl_lines="40-48"
|
||||||
--8<-- "../docs_src/queries/docs003.py"
|
--8<-- "../docs_src/queries/docs003.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -122,7 +125,7 @@ Allows you to create multiple objects at once.
|
|||||||
|
|
||||||
A valid list of `Model` objects needs to be passed.
|
A valid list of `Model` objects needs to be passed.
|
||||||
|
|
||||||
```python hl_lines="21-27"
|
```python hl_lines="26-32"
|
||||||
--8<-- "../docs_src/queries/docs004.py"
|
--8<-- "../docs_src/queries/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ If you do not provide this flag or a filter a `QueryDefinitionError` will be rai
|
|||||||
|
|
||||||
Return number of rows deleted.
|
Return number of rows deleted.
|
||||||
|
|
||||||
```python hl_lines="26-30"
|
```python hl_lines="40-44"
|
||||||
--8<-- "../docs_src/queries/docs005.py"
|
--8<-- "../docs_src/queries/docs005.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -59,20 +59,14 @@ If you specify the keep_reversed flag to `False` `ormar` will also delete the re
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "tracks"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -104,20 +98,14 @@ If you specify the keep_reversed flag to `False` `ormar` will also delete the re
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "tracks"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
|
|||||||
@ -35,20 +35,14 @@ a filter across an FK relationship.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "tracks"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -197,20 +191,14 @@ conditions.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "tracks"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -245,26 +233,21 @@ Since it sounds more complicated than it is, let's look at some examples.
|
|||||||
|
|
||||||
Given a sample models like this:
|
Given a sample models like this:
|
||||||
```python
|
```python
|
||||||
database = databases.Database(DATABASE_URL)
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "authors"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "books"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
@ -721,7 +704,7 @@ Ordering in sql will be applied in order of names you provide in order_by.
|
|||||||
Given sample Models like following:
|
Given sample Models like following:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
--8 < -- "../../docs_src/queries/docs007.py"
|
--8<-- "../docs_src/queries/docs007.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
To order by main model field just provide a field name
|
To order by main model field just provide a field name
|
||||||
@ -808,7 +791,7 @@ Since order of rows in a database is not guaranteed, `ormar` **always** issues a
|
|||||||
|
|
||||||
When querying the database with given model by default the `Model` is ordered by the `primary_key`
|
When querying the database with given model by default the `Model` is ordered by the `primary_key`
|
||||||
column ascending. If you wish to change the default behaviour you can do it by providing `orders_by`
|
column ascending. If you wish to change the default behaviour you can do it by providing `orders_by`
|
||||||
parameter to model `Meta` class.
|
parameter to `ormar_config`.
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about models sort order visit [models](../models/index.md#model-sort-order) section of documentation
|
To read more about models sort order visit [models](../models/index.md#model-sort-order) section of documentation
|
||||||
@ -823,8 +806,8 @@ Order in which order_by clauses are applied is as follows:
|
|||||||
|
|
||||||
* Explicitly passed `order_by()` calls in query
|
* Explicitly passed `order_by()` calls in query
|
||||||
* Relation passed `orders_by` and `related_orders_by` if exists
|
* Relation passed `orders_by` and `related_orders_by` if exists
|
||||||
* Model `Meta` class `orders_by`
|
* Model's `ormar_config` object `orders_by`
|
||||||
* Model `primary_key` column ascending (fallback, used if none of above provided)
|
* Model's `primary_key` column ascending (fallback, used if none of above provided)
|
||||||
|
|
||||||
**Order from only one source is applied to each `Model` (so that you can always overwrite it in a single query).**
|
**Order from only one source is applied to each `Model` (so that you can always overwrite it in a single query).**
|
||||||
|
|
||||||
|
|||||||
@ -46,20 +46,14 @@ To chain related `Models` relation use double underscores between names.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "tracks"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -82,10 +76,7 @@ You can provide a string or a list of strings (or a field/ list of fields)
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class SchoolClass(ormar.Model):
|
class SchoolClass(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="schoolclasses")
|
||||||
tablename = "schoolclasses"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -93,20 +84,14 @@ class SchoolClass(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Student(ormar.Model):
|
class Student(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "students"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -115,10 +100,7 @@ class Student(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Teacher(ormar.Model):
|
class Teacher(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "teachers"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -182,18 +164,14 @@ If `follow=True` is set it adds also related models of related models.
|
|||||||
With sample date like follow:
|
With sample date like follow:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
base_ormar_config = OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL, force_rollback=True),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
class BaseMeta(ormar.ModelMeta):
|
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
|
|
||||||
class Address(ormar.Model):
|
class Address(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="addresses")
|
||||||
tablename = "addresses"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
street: str = ormar.String(max_length=100, nullable=False)
|
street: str = ormar.String(max_length=100, nullable=False)
|
||||||
@ -203,8 +181,7 @@ class Address(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Branch(ormar.Model):
|
class Branch(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="branches")
|
||||||
tablename = "branches"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, nullable=False)
|
name: str = ormar.String(max_length=100, nullable=False)
|
||||||
@ -212,8 +189,7 @@ class Branch(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Company(ormar.Model):
|
class Company(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="companies")
|
||||||
tablename = "companies"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100, nullable=False, name="company_name")
|
name: str = ormar.String(max_length=100, nullable=False, name="company_name")
|
||||||
@ -264,20 +240,14 @@ To chain related `Models` relation use double underscores between names.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "tracks"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -301,10 +271,7 @@ You can provide a string, or a list of strings
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class SchoolClass(ormar.Model):
|
class SchoolClass(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="schoolclasses")
|
||||||
tablename = "schoolclasses"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -312,20 +279,14 @@ class SchoolClass(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Student(ormar.Model):
|
class Student(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "students"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -334,10 +295,7 @@ class Student(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Teacher(ormar.Model):
|
class Teacher(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "teachers"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -432,12 +390,12 @@ might be faster despite it needs to perform three separate queries instead of on
|
|||||||
|
|
||||||
#### Memory
|
#### Memory
|
||||||
|
|
||||||
`ormar` is a mini ORM meaning that it does not keep a registry of already loaded models.
|
`ormar` is does not keep a registry of already loaded models.
|
||||||
|
|
||||||
That means that in `select_related` example above you will always have 10 000 Models A,
|
That means that in `select_related` example above you will always have 10 000 Models A,
|
||||||
30 000 Models B
|
30 000 Models B
|
||||||
(even if the unique number of rows in db is 3 - processing of `select_related` spawns **
|
(even if the unique number of rows in db is 3 - processing of `select_related` spawns
|
||||||
new** child models for each parent model). And 60 000 Models C.
|
**new** child models for each parent model). And 60 000 Models C.
|
||||||
|
|
||||||
If the same Model B is shared by rows 1, 10, 100 etc. and you update one of those, the
|
If the same Model B is shared by rows 1, 10, 100 etc. and you update one of those, the
|
||||||
rest of rows that share the same child will **not** be updated on the spot. If you
|
rest of rows that share the same child will **not** be updated on the spot. If you
|
||||||
@ -471,7 +429,7 @@ that `select_related` will use more memory as each child is instantiated as a ne
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
# will return False (note that id is a python `builtin` function not ormar one).
|
# will return False (note that id is a python `builtin` function not ormar one).
|
||||||
id(row1.child1) == (ro100.child1)
|
id(row1.child1) == id(ro100.child1)
|
||||||
|
|
||||||
# from above - will also return False
|
# from above - will also return False
|
||||||
id(model1) == id(model2)
|
id(model1) == id(model2)
|
||||||
|
|||||||
@ -22,10 +22,11 @@ Combines the `offset` and `limit` methods based on page number and size
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
tablename="track"
|
tablename="track"
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -52,10 +53,11 @@ use the `limit_raw_sql` parameter flag, and set it to `True`.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
tablename="track"
|
tablename="track"
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -86,10 +88,11 @@ use the `limit_raw_sql` parameter flag, and set it to `True`.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
tablename="track"
|
tablename="track"
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
|
|||||||
@ -40,8 +40,7 @@ Example:
|
|||||||
# declared models
|
# declared models
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
@ -49,8 +48,7 @@ class Category(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "posts"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200)
|
name: str = ormar.String(max_length=200)
|
||||||
@ -83,16 +81,14 @@ Note how nested models columns will be prefixed with full relation path coming f
|
|||||||
# declare models
|
# declare models
|
||||||
|
|
||||||
class User(ormar.Model):
|
class User(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Role(ormar.Model):
|
class Role(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -216,8 +212,7 @@ Example:
|
|||||||
# declared models
|
# declared models
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
@ -225,8 +220,7 @@ class Category(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "posts"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=200)
|
name: str = ormar.String(max_length=200)
|
||||||
@ -257,8 +251,7 @@ Let's complicate the relation and modify the previously mentioned Category model
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
|
|||||||
@ -31,10 +31,11 @@ Passing a criteria is actually calling filter(*args, **kwargs) method described
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
tablename="track"
|
tablename="track"
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -74,10 +75,7 @@ a new one with given kwargs and _defaults.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="album")
|
||||||
tablename = "album"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -114,10 +112,7 @@ Gets the first row from the db ordered by primary key column ascending.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="album")
|
||||||
tablename = "album"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -143,20 +138,14 @@ If there are no rows meeting the criteria an empty list is returned.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="album")
|
||||||
tablename = "album"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="track")
|
||||||
tablename = "track"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
@ -186,10 +175,7 @@ If there are no rows meeting the criteria an empty async generator is returned.
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="album")
|
||||||
tablename = "album"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -24,52 +24,7 @@ With `fields()` you can select subset of model columns to limit the data load.
|
|||||||
Given a sample data like following:
|
Given a sample data like following:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import databases
|
--8<-- "../docs_src/select_columns/docs001.py"
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
|
||||||
from tests.settings import DATABASE_URL
|
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
|
||||||
|
|
||||||
class Company(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename = "companies"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
|
||||||
name: str = ormar.String(max_length=100)
|
|
||||||
founded: int = ormar.Integer(nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Car(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename = "cars"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
|
||||||
manufacturer = ormar.ForeignKey(Company)
|
|
||||||
name: str = ormar.String(max_length=100)
|
|
||||||
year: int = ormar.Integer(nullable=True)
|
|
||||||
gearbox_type: str = ormar.String(max_length=20, nullable=True)
|
|
||||||
gears: int = ormar.Integer(nullable=True)
|
|
||||||
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
# build some sample data
|
|
||||||
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
|
||||||
await Car.objects.create(manufacturer=toyota, name="Corolla", year=2020, gearbox_type='Manual', gears=5,
|
|
||||||
aircon_type='Manual')
|
|
||||||
await Car.objects.create(manufacturer=toyota, name="Yaris", year=2019, gearbox_type='Manual', gears=5,
|
|
||||||
aircon_type='Manual')
|
|
||||||
await Car.objects.create(manufacturer=toyota, name="Supreme", year=2020, gearbox_type='Auto', gears=6,
|
|
||||||
aircon_type='Auto')
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can select specified fields by passing a `str, List[str], Set[str] or dict` with
|
You can select specified fields by passing a `str, List[str], Set[str] or dict` with
|
||||||
@ -78,8 +33,13 @@ nested definition.
|
|||||||
To include related models use
|
To include related models use
|
||||||
notation `{related_name}__{column}[__{optional_next} etc.]`.
|
notation `{related_name}__{column}[__{optional_next} etc.]`.
|
||||||
|
|
||||||
```python hl_lines="1"
|
```python hl_lines="1-6"
|
||||||
all_cars = await Car.objects.select_related('manufacturer').fields(['id', 'name', 'manufacturer__name']).all()
|
all_cars = await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields(['id', 'name', 'manufacturer__name'])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
for car in all_cars:
|
for car in all_cars:
|
||||||
# excluded columns will yield None
|
# excluded columns will yield None
|
||||||
assert all(getattr(car, x) is None for x in ['year', 'gearbox_type', 'gears', 'aircon_type'])
|
assert all(getattr(car, x) is None for x in ['year', 'gearbox_type', 'gears', 'aircon_type'])
|
||||||
@ -97,9 +57,14 @@ for those models in fields
|
|||||||
|
|
||||||
- implies a list of all fields for those nested models.
|
- implies a list of all fields for those nested models.
|
||||||
|
|
||||||
```python hl_lines="1"
|
```python hl_lines="1-7"
|
||||||
all_cars = await Car.objects.select_related('manufacturer').fields('id').fields(
|
all_cars = await (
|
||||||
['name']).all()
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields('id')
|
||||||
|
.fields(['name'])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# all fields from company model are selected
|
# all fields from company model are selected
|
||||||
assert all_cars[0].manufacturer.name == 'Toyota'
|
assert all_cars[0].manufacturer.name == 'Toyota'
|
||||||
assert all_cars[0].manufacturer.founded == 1937
|
assert all_cars[0].manufacturer.founded == 1937
|
||||||
@ -115,8 +80,12 @@ assert all_cars[0].manufacturer.founded == 1937
|
|||||||
You cannot exclude mandatory model columns - `manufacturer__name` in this example.
|
You cannot exclude mandatory model columns - `manufacturer__name` in this example.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
await Car.objects.select_related('manufacturer').fields(
|
await (
|
||||||
['id', 'name', 'manufacturer__founded']).all()
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields(['id', 'name', 'manufacturer__founded'])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# will raise pydantic ValidationError as company.name is required
|
# will raise pydantic ValidationError as company.name is required
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -138,38 +107,71 @@ Below you can see examples that are equivalent:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
# 1. like in example above
|
# 1. like in example above
|
||||||
await Car.objects.select_related('manufacturer').fields(['id', 'name', 'manufacturer__name']).all()
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields(['id', 'name', 'manufacturer__name'])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# 2. to mark a field as required use ellipsis
|
# 2. to mark a field as required use ellipsis
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields({'id': ...,
|
||||||
'name': ...,
|
'name': ...,
|
||||||
'manufacturer': {
|
'manufacturer': {
|
||||||
'name': ...}
|
'name': ...
|
||||||
}).all()
|
}
|
||||||
|
})
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# 3. to include whole nested model use ellipsis
|
# 3. to include whole nested model use ellipsis
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields({'id': ...,
|
||||||
'name': ...,
|
'name': ...,
|
||||||
'manufacturer': ...
|
'manufacturer': ...
|
||||||
}).all()
|
})
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# 4. to specify fields at last nesting level you can also use set - equivalent to 2. above
|
# 4. to specify fields at last nesting level
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
# you can also use set - equivalent to 2. above
|
||||||
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields({'id': ...,
|
||||||
'name': ...,
|
'name': ...,
|
||||||
'manufacturer': {'name'}
|
'manufacturer': {'name'}
|
||||||
}).all()
|
})
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# 5. of course set can have multiple fields
|
# 5. of course set can have multiple fields
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields({'id': ...,
|
||||||
'name': ...,
|
'name': ...,
|
||||||
'manufacturer': {'name', 'founded'}
|
'manufacturer': {'name', 'founded'}
|
||||||
}).all()
|
})
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
# 6. you can include all nested fields but it will be equivalent of 3. above which is shorter
|
# 6. you can include all nested fields,
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
# but it will be equivalent of 3. above which is shorter
|
||||||
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.fields({'id': ...,
|
||||||
'name': ...,
|
'name': ...,
|
||||||
'manufacturer': {'id', 'name', 'founded'}
|
'manufacturer': {'id', 'name', 'founded'}
|
||||||
}).all()
|
})
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -201,74 +203,65 @@ exclude fields from whole hierarchy.
|
|||||||
|
|
||||||
Below you can find few simple examples:
|
Below you can find few simple examples:
|
||||||
|
|
||||||
```python hl_lines="47 48 60 61 67"
|
```python
|
||||||
import databases
|
--8<-- "../docs_src/select_columns/docs001.py"
|
||||||
import sqlalchemy
|
```
|
||||||
|
|
||||||
import ormar
|
```python
|
||||||
from tests.settings import DATABASE_URL
|
# select manufacturer but only name,
|
||||||
|
# to include related models use notation {model_name}__{column}
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
all_cars = await (
|
||||||
metadata = sqlalchemy.MetaData()
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.exclude_fields([
|
||||||
class Company(ormar.Model):
|
'year',
|
||||||
class Meta:
|
'gearbox_type',
|
||||||
tablename = "companies"
|
'gears',
|
||||||
metadata = metadata
|
'aircon_type',
|
||||||
database = database
|
'company__founded'
|
||||||
|
])
|
||||||
id: int = ormar.Integer(primary_key=True)
|
.all()
|
||||||
name: str = ormar.String(max_length=100)
|
)
|
||||||
founded: int = ormar.Integer(nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
class Car(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
tablename = "cars"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
|
||||||
manufacturer = ormar.ForeignKey(Company)
|
|
||||||
name: str = ormar.String(max_length=100)
|
|
||||||
year: int = ormar.Integer(nullable=True)
|
|
||||||
gearbox_type: str = ormar.String(max_length=20, nullable=True)
|
|
||||||
gears: int = ormar.Integer(nullable=True)
|
|
||||||
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
|
||||||
|
|
||||||
|
|
||||||
# build some sample data
|
|
||||||
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
|
||||||
await Car.objects.create(manufacturer=toyota, name="Corolla", year=2020, gearbox_type='Manual', gears=5,
|
|
||||||
aircon_type='Manual')
|
|
||||||
await Car.objects.create(manufacturer=toyota, name="Yaris", year=2019, gearbox_type='Manual', gears=5,
|
|
||||||
aircon_type='Manual')
|
|
||||||
await Car.objects.create(manufacturer=toyota, name="Supreme", year=2020, gearbox_type='Auto', gears=6,
|
|
||||||
aircon_type='Auto')
|
|
||||||
|
|
||||||
# select manufacturer but only name - to include related models use notation {model_name}__{column}
|
|
||||||
all_cars = await Car.objects.select_related('manufacturer').exclude_fields(
|
|
||||||
['year', 'gearbox_type', 'gears', 'aircon_type', 'company__founded']).all()
|
|
||||||
for car in all_cars:
|
for car in all_cars:
|
||||||
# excluded columns will yield None
|
# excluded columns will yield None
|
||||||
assert all(getattr(car, x) is None for x in ['year', 'gearbox_type', 'gears', 'aircon_type'])
|
assert all(getattr(car, x) is None
|
||||||
# included column on related models will be available, pk column is always included
|
for x in [
|
||||||
|
'year',
|
||||||
|
'gearbox_type',
|
||||||
|
'gears',
|
||||||
|
'aircon_type'
|
||||||
|
])
|
||||||
|
# included column on related models will be available,
|
||||||
|
# pk column is always included
|
||||||
# even if you do not include it in fields list
|
# even if you do not include it in fields list
|
||||||
assert car.manufacturer.name == 'Toyota'
|
assert car.manufacturer.name == 'Toyota'
|
||||||
# also in the nested related models - you cannot exclude pk - it's always auto added
|
# also in the nested related models,
|
||||||
|
# you cannot exclude pk - it's always auto added
|
||||||
assert car.manufacturer.founded is None
|
assert car.manufacturer.founded is None
|
||||||
|
|
||||||
# fields() can be called several times, building up the columns to select
|
# fields() can be called several times,
|
||||||
# models selected in select_related but with no columns in fields list implies all fields
|
# building up the columns to select
|
||||||
all_cars = await Car.objects.select_related('manufacturer').exclude_fields('year').exclude_fields(
|
# models included in select_related
|
||||||
['gear', 'gearbox_type']).all()
|
# but with no columns in fields list implies all fields
|
||||||
|
all_cars = await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.exclude_fields('year')
|
||||||
|
.exclude_fields(['gear', 'gearbox_type'])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# all fields from company model are selected
|
# all fields from company model are selected
|
||||||
assert all_cars[0].manufacturer.name == 'Toyota'
|
assert all_cars[0].manufacturer.name == 'Toyota'
|
||||||
assert all_cars[0].manufacturer.founded == 1937
|
assert all_cars[0].manufacturer.founded == 1937
|
||||||
|
|
||||||
# cannot exclude mandatory model columns - company__name in this example - note usage of dict/set this time
|
# cannot exclude mandatory model columns,
|
||||||
await Car.objects.select_related('manufacturer').exclude_fields([{'company': {'name'}}]).all()
|
# company__name in this example - note usage of dict/set this time
|
||||||
|
await (
|
||||||
|
Car.objects
|
||||||
|
.select_related('manufacturer')
|
||||||
|
.exclude_fields([{'company': {'name'}}])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
# will raise pydantic ValidationError as company.name is required
|
# will raise pydantic ValidationError as company.name is required
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -29,7 +29,7 @@ If you do not provide this flag or a filter a `QueryDefinitionError` will be rai
|
|||||||
|
|
||||||
Return number of rows updated.
|
Return number of rows updated.
|
||||||
|
|
||||||
```Python hl_lines="26-28"
|
```Python hl_lines="42-44"
|
||||||
--8<-- "../docs_src/queries/docs002.py"
|
--8<-- "../docs_src/queries/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ Return number of rows updated.
|
|||||||
|
|
||||||
Updates the model, or in case there is no match in database creates a new one.
|
Updates the model, or in case there is no match in database creates a new one.
|
||||||
|
|
||||||
```Python hl_lines="26-32"
|
```Python hl_lines="40-48"
|
||||||
--8<-- "../docs_src/queries/docs003.py"
|
--8<-- "../docs_src/queries/docs003.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ Sqlalchemy column and Type are automatically taken from target `Model`.
|
|||||||
|
|
||||||
To define a relation add `ForeignKey` field that points to related `Model`.
|
To define a relation add `ForeignKey` field that points to related `Model`.
|
||||||
|
|
||||||
```Python hl_lines="29"
|
```Python hl_lines="30"
|
||||||
--8<-- "../docs_src/fields/docs003.py"
|
--8<-- "../docs_src/fields/docs003.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ To define a relation add `ForeignKey` field that points to related `Model`.
|
|||||||
|
|
||||||
By default it's child (source) `Model` name + s, like courses in snippet below:
|
By default it's child (source) `Model` name + s, like courses in snippet below:
|
||||||
|
|
||||||
```Python hl_lines="29 35"
|
```Python hl_lines="29 36"
|
||||||
--8<-- "../docs_src/fields/docs001.py"
|
--8<-- "../docs_src/fields/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -45,15 +45,14 @@ But you cannot:
|
|||||||
|
|
||||||
* Access the related field from reverse model with `related_name`
|
* Access the related field from reverse model with `related_name`
|
||||||
* Even if you `select_related` from reverse side of the model the returned models won't be populated in reversed instance (the join is not prevented so you still can `filter` and `order_by` over the relation)
|
* Even if you `select_related` from reverse side of the model the returned models won't be populated in reversed instance (the join is not prevented so you still can `filter` and `order_by` over the relation)
|
||||||
* The relation won't be populated in `dict()` and `json()`
|
* The relation won't be populated in `model_dump()` and `model_dump_json()`
|
||||||
* You cannot pass the nested related objects when populating from dictionary or json (also through `fastapi`). It will be either ignored or error will be raised depending on `extra` setting in pydantic `Config`.
|
* You cannot pass the nested related objects when populating from dictionary or json (also through `fastapi`). It will be either ignored or error will be raised depending on `extra` setting in pydantic `Config`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
first_name: str = ormar.String(max_length=80)
|
first_name: str = ormar.String(max_length=80)
|
||||||
@ -61,8 +60,7 @@ class Author(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
@ -82,8 +80,8 @@ authors = (
|
|||||||
assert authors[0].first_name == "Test"
|
assert authors[0].first_name == "Test"
|
||||||
|
|
||||||
# note that posts are not populated for author even if explicitly
|
# note that posts are not populated for author even if explicitly
|
||||||
# included in select_related - note no posts in dict()
|
# included in select_related - note no posts in model_dump()
|
||||||
assert author.dict(exclude={"id"}) == {"first_name": "Test", "last_name": "Author"}
|
assert author.model_dump(exclude={"id"}) == {"first_name": "Test", "last_name": "Author"}
|
||||||
|
|
||||||
# still can filter through fields of related model
|
# still can filter through fields of related model
|
||||||
authors = await Author.objects.filter(posts__title="Test Post").all()
|
authors = await Author.objects.filter(posts__title="Test Post").all()
|
||||||
@ -112,7 +110,7 @@ assert department.courses[0] == course
|
|||||||
!!!warning
|
!!!warning
|
||||||
If you want to add child model on related model the primary key value for parent model **has to exist in database**.
|
If you want to add child model on related model the primary key value for parent model **has to exist in database**.
|
||||||
|
|
||||||
Otherwise ormar will raise RelationshipInstanceError as it cannot set child's ForeignKey column value
|
Otherwise ormar will raise `RelationshipInstanceError` as it cannot set child's ForeignKey column value
|
||||||
if parent model has no primary key value.
|
if parent model has no primary key value.
|
||||||
|
|
||||||
That means that in example above the department has to be saved before you can call `department.courses.add()`.
|
That means that in example above the department has to be saved before you can call `department.courses.add()`.
|
||||||
@ -151,7 +149,7 @@ await department.courses.remove(course, keep_reversed=False)
|
|||||||
|
|
||||||
Removal of all related models in one call.
|
Removal of all related models in one call.
|
||||||
|
|
||||||
Like remove by default `clear()` nulls the ForeigKey column on child model (all, not matter if they are loaded or not).
|
Like with remove, by default, `clear()` nulls the ForeigKey column on child model (all, not matter if they are loaded or not).
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# nulls department column on all courses related to this department
|
# nulls department column on all courses related to this department
|
||||||
@ -173,9 +171,9 @@ To read which methods of QuerySet are available read below [querysetproxy][query
|
|||||||
|
|
||||||
## related_name
|
## related_name
|
||||||
|
|
||||||
But you can overwrite this name by providing `related_name` parameter like below:
|
You can overwrite related model field name by providing `related_name` parameter like below:
|
||||||
|
|
||||||
```Python hl_lines="29 35"
|
```Python hl_lines="27-29 35"
|
||||||
--8<-- "../docs_src/fields/docs002.py"
|
--8<-- "../docs_src/fields/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -230,7 +228,7 @@ You have several ways to set-up a relationship connection.
|
|||||||
|
|
||||||
The most obvious one is to pass a related `Model` instance to the constructor.
|
The most obvious one is to pass a related `Model` instance to the constructor.
|
||||||
|
|
||||||
```Python hl_lines="34-35"
|
```Python hl_lines="35-36"
|
||||||
--8<-- "../docs_src/relations/docs001.py"
|
--8<-- "../docs_src/relations/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -238,7 +236,7 @@ The most obvious one is to pass a related `Model` instance to the constructor.
|
|||||||
|
|
||||||
You can setup the relation also with just the pk column value of the related model.
|
You can setup the relation also with just the pk column value of the related model.
|
||||||
|
|
||||||
```Python hl_lines="37-38"
|
```Python hl_lines="38-39"
|
||||||
--8<-- "../docs_src/relations/docs001.py"
|
--8<-- "../docs_src/relations/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -246,9 +244,9 @@ You can setup the relation also with just the pk column value of the related mod
|
|||||||
|
|
||||||
Next option is with a dictionary of key-values of the related model.
|
Next option is with a dictionary of key-values of the related model.
|
||||||
|
|
||||||
You can build the dictionary yourself or get it from existing model with `dict()` method.
|
You can build the dictionary yourself or get it from existing model with `model_dump()` method.
|
||||||
|
|
||||||
```Python hl_lines="40-41"
|
```Python hl_lines="41-42"
|
||||||
--8<-- "../docs_src/relations/docs001.py"
|
--8<-- "../docs_src/relations/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -256,7 +254,7 @@ You can build the dictionary yourself or get it from existing model with `dict()
|
|||||||
|
|
||||||
Finally you can explicitly set it to None (default behavior if no value passed).
|
Finally you can explicitly set it to None (default behavior if no value passed).
|
||||||
|
|
||||||
```Python hl_lines="43-44"
|
```Python hl_lines="44-45"
|
||||||
--8<-- "../docs_src/relations/docs001.py"
|
--8<-- "../docs_src/relations/docs001.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ To read more about methods, possibilities, definition etc. please read the subse
|
|||||||
|
|
||||||
To define many-to-one relation use `ForeignKey` field.
|
To define many-to-one relation use `ForeignKey` field.
|
||||||
|
|
||||||
```Python hl_lines="17"
|
```Python hl_lines="26"
|
||||||
--8<-- "../docs_src/relations/docs003.py"
|
--8<-- "../docs_src/relations/docs003.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -24,13 +24,11 @@ To define many-to-one relation use `ForeignKey` field.
|
|||||||
|
|
||||||
The definition of one-to-many relation also uses `ForeignKey`, and it's registered for you automatically.
|
The definition of one-to-many relation also uses `ForeignKey`, and it's registered for you automatically.
|
||||||
|
|
||||||
So in relation ato example above.
|
So in relation to example above.
|
||||||
|
|
||||||
```Python hl_lines="17"
|
```Python hl_lines="7-8"
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -52,21 +50,22 @@ class Department(ormar.Model):
|
|||||||
|
|
||||||
To define many-to-many relation use `ManyToMany` field.
|
To define many-to-many relation use `ManyToMany` field.
|
||||||
|
|
||||||
```python hl_lines="18"
|
```python hl_lines="19"
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "categories"
|
database=database,
|
||||||
database = database
|
metadata=metadata,
|
||||||
metadata = metadata
|
tablename="categories",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "posts"
|
database=database,
|
||||||
database = database
|
metadata=metadata,
|
||||||
metadata = metadata
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
@ -92,18 +91,24 @@ side of the current query for m2m models.
|
|||||||
So if you query from model `A` to model `B`, only model `B` has through field exposed.
|
So if you query from model `A` to model `B`, only model `B` has through field exposed.
|
||||||
Which kind of make sense, since it's a one through model/field for each of related models.
|
Which kind of make sense, since it's a one through model/field for each of related models.
|
||||||
|
|
||||||
```python hl_lines="10-15"
|
```python hl_lines="12-21"
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "categories"
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="categories",
|
||||||
|
)
|
||||||
|
|
||||||
id = ormar.Integer(primary_key=True)
|
id = ormar.Integer(primary_key=True)
|
||||||
name = ormar.String(max_length=40)
|
name = ormar.String(max_length=40)
|
||||||
|
|
||||||
# you can specify additional fields on through model
|
# you can specify additional fields on through model
|
||||||
class PostCategory(ormar.Model):
|
class PostCategory(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "posts_x_categories"
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="posts_x_categories",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
sort_order: int = ormar.Integer(nullable=True)
|
sort_order: int = ormar.Integer(nullable=True)
|
||||||
@ -111,8 +116,10 @@ class PostCategory(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = ormar.OrmarConfig(
|
||||||
pass
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
@ -130,7 +137,7 @@ class Post(ormar.Model):
|
|||||||
|
|
||||||
## Relationship default sort order
|
## Relationship default sort order
|
||||||
|
|
||||||
By default relations follow model default sort order so `primary_key` column ascending, or any sort order se in `Meta` class.
|
By default relations follow model default sort order so `primary_key` column ascending, or any sort order se in `ormar_config` object.
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To read more about models sort order visit [models](../models/index.md#model-sort-order) section of documentation
|
To read more about models sort order visit [models](../models/index.md#model-sort-order) section of documentation
|
||||||
@ -143,27 +150,26 @@ columns also `Through` model columns `{through_field_name}__{column_name}`
|
|||||||
|
|
||||||
Sample configuration might look like this:
|
Sample configuration might look like this:
|
||||||
|
|
||||||
```python hl_lines="24"
|
```python hl_lines="23"
|
||||||
database = databases.Database(DATABASE_URL)
|
database = databases.Database(DATABASE_URL)
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = metadata
|
database=database,
|
||||||
database = database
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "authors"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "books"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Optional[Author] = ormar.ForeignKey(
|
author: Optional[Author] = ormar.ForeignKey(
|
||||||
@ -186,14 +192,12 @@ In order to create auto-relation or create two models that reference each other
|
|||||||
different relations (remember the reverse side is auto-registered for you), you need to use
|
different relations (remember the reverse side is auto-registered for you), you need to use
|
||||||
`ForwardRef` from `typing` module.
|
`ForwardRef` from `typing` module.
|
||||||
|
|
||||||
```python hl_lines="1 11 14"
|
```python hl_lines="1 9 12"
|
||||||
PersonRef = ForwardRef("Person")
|
PersonRef = ForwardRef("Person")
|
||||||
|
|
||||||
|
|
||||||
class Person(ormar.Model):
|
class Person(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ Sqlalchemy column and Type are automatically taken from target `Model`.
|
|||||||
|
|
||||||
## Defining Models
|
## Defining Models
|
||||||
|
|
||||||
```Python hl_lines="40"
|
```Python hl_lines="34"
|
||||||
--8<-- "../docs_src/relations/docs002.py"
|
--8<-- "../docs_src/relations/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -24,20 +24,18 @@ news = await Category.objects.create(name="News")
|
|||||||
|
|
||||||
`ForeignKey` fields are automatically registering reverse side of the relation.
|
`ForeignKey` fields are automatically registering reverse side of the relation.
|
||||||
|
|
||||||
By default it's child (source) `Model` name + s, like courses in snippet below:
|
By default it's child (source) `Model` name + s, like `posts` in snippet below:
|
||||||
|
|
||||||
```python
|
```python hl_lines="25-26"
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
@ -83,29 +81,29 @@ flag of the `ManyToMany`.
|
|||||||
|
|
||||||
If you set `skip_reverse` flag internally the field is still registered on the other
|
If you set `skip_reverse` flag internally the field is still registered on the other
|
||||||
side of the relationship so you can:
|
side of the relationship so you can:
|
||||||
|
|
||||||
* `filter` by related models fields from reverse model
|
* `filter` by related models fields from reverse model
|
||||||
* `order_by` by related models fields from reverse model
|
* `order_by` by related models fields from reverse model
|
||||||
|
|
||||||
But you cannot:
|
But you cannot:
|
||||||
|
|
||||||
* access the related field from reverse model with `related_name`
|
* access the related field from reverse model with `related_name`
|
||||||
* even if you `select_related` from reverse side of the model the returned models won't be populated in reversed instance (the join is not prevented so you still can `filter` and `order_by` over the relation)
|
* even if you `select_related` from reverse side of the model the returned models won't be populated in reversed instance (the join is not prevented so you still can `filter` and `order_by` over the relation)
|
||||||
* the relation won't be populated in `dict()` and `json()`
|
* the relation won't be populated in `model_dump()` and `json()`
|
||||||
* you cannot pass the nested related objects when populating from dictionary or json (also through `fastapi`). It will be either ignored or error will be raised depending on `extra` setting in pydantic `Config`.
|
* you cannot pass the nested related objects when populating from dictionary or json (also through `fastapi`). It will be either ignored or error will be raised depending on `extra` setting in pydantic `Config`.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
@ -126,8 +124,8 @@ categories = (
|
|||||||
assert categories[0].first_name == "Test"
|
assert categories[0].first_name == "Test"
|
||||||
|
|
||||||
# note that posts are not populated for author even if explicitly
|
# note that posts are not populated for author even if explicitly
|
||||||
# included in select_related - note no posts in dict()
|
# included in select_related - note no posts in model_dump()
|
||||||
assert news.dict(exclude={"id"}) == {"name": "News"}
|
assert news.model_dump(exclude={"id"}) == {"name": "News"}
|
||||||
|
|
||||||
# still can filter through fields of related model
|
# still can filter through fields of related model
|
||||||
categories = await Category.objects.filter(posts__title="Hello, M2M").all()
|
categories = await Category.objects.filter(posts__title="Hello, M2M").all()
|
||||||
@ -141,7 +139,7 @@ assert len(categories) == 1
|
|||||||
Optionally if you want to add additional fields you can explicitly create and pass
|
Optionally if you want to add additional fields you can explicitly create and pass
|
||||||
the through model class.
|
the through model class.
|
||||||
|
|
||||||
```Python hl_lines="14-20 29"
|
```Python hl_lines="19-24 32"
|
||||||
--8<-- "../docs_src/relations/docs004.py"
|
--8<-- "../docs_src/relations/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -170,9 +168,7 @@ So in example like this:
|
|||||||
```python
|
```python
|
||||||
... # course declaration omitted
|
... # course declaration omitted
|
||||||
class Student(ormar.Model):
|
class Student(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -180,10 +176,7 @@ class Student(ormar.Model):
|
|||||||
|
|
||||||
# will produce default Through model like follows (example simplified)
|
# will produce default Through model like follows (example simplified)
|
||||||
class StudentCourse(ormar.Model):
|
class StudentCourse(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="students_courses")
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
tablename = "students_courses"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
student = ormar.ForeignKey(Student) # default name
|
student = ormar.ForeignKey(Student) # default name
|
||||||
@ -199,10 +192,14 @@ Example:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
... # course declaration omitted
|
... # course declaration omitted
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database("sqlite:///db.sqlite"),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Student(ormar.Model):
|
class Student(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -212,10 +209,7 @@ class Student(ormar.Model):
|
|||||||
|
|
||||||
# will produce Through model like follows (example simplified)
|
# will produce Through model like follows (example simplified)
|
||||||
class StudentCourse(ormar.Model):
|
class StudentCourse(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="student_courses")
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
tablename = "students_courses"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
student_id = ormar.ForeignKey(Student) # set by through_relation_name
|
student_id = ormar.ForeignKey(Student) # set by through_relation_name
|
||||||
@ -238,7 +232,7 @@ so it's useful only when additional fields are provided on `Through` model.
|
|||||||
|
|
||||||
In a sample model setup as following:
|
In a sample model setup as following:
|
||||||
|
|
||||||
```Python hl_lines="14-20 29"
|
```Python hl_lines="19-24 32"
|
||||||
--8<-- "../docs_src/relations/docs004.py"
|
--8<-- "../docs_src/relations/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -14,21 +14,8 @@ First, you need to import the required ref from typing.
|
|||||||
from typing import ForwardRef
|
from typing import ForwardRef
|
||||||
```
|
```
|
||||||
|
|
||||||
But note that before python 3.7 it used to be internal, so for python <= 3.6 you need
|
|
||||||
|
|
||||||
```python
|
|
||||||
from typing import _ForwardRef as ForwardRef
|
|
||||||
```
|
|
||||||
|
|
||||||
or since `pydantic` is required by `ormar` it can handle this switch for you.
|
|
||||||
In that case you can simply import ForwardRef from pydantic regardless of your python version.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from pydantic.typing import ForwardRef
|
|
||||||
```
|
|
||||||
|
|
||||||
Now we need a sample model and a reference to the same model,
|
Now we need a sample model and a reference to the same model,
|
||||||
which will be used to creat a self referencing relation.
|
which will be used to create a self referencing relation.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# create the forwardref to model Person
|
# create the forwardref to model Person
|
||||||
@ -36,9 +23,7 @@ PersonRef = ForwardRef("Person")
|
|||||||
|
|
||||||
|
|
||||||
class Person(ormar.Model):
|
class Person(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -72,9 +57,7 @@ PersonRef = ForwardRef("Person")
|
|||||||
|
|
||||||
|
|
||||||
class Person(ormar.Model):
|
class Person(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -93,14 +76,10 @@ and through parameters.
|
|||||||
ChildRef = ForwardRef("Child")
|
ChildRef = ForwardRef("Child")
|
||||||
|
|
||||||
class ChildFriend(ormar.Model):
|
class ChildFriend(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
class Child(ormar.Model):
|
class Child(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -132,9 +111,7 @@ TeacherRef = ForwardRef("Teacher")
|
|||||||
|
|
||||||
|
|
||||||
class Student(ormar.Model):
|
class Student(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -144,16 +121,11 @@ class Student(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class StudentTeacher(ormar.Model):
|
class StudentTeacher(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy(tablename='students_x_teachers')
|
||||||
tablename = 'students_x_teachers'
|
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
|
|
||||||
class Teacher(ormar.Model):
|
class Teacher(ormar.Model):
|
||||||
class Meta(ModelMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
metadata = metadata
|
|
||||||
database = db
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -58,7 +58,7 @@ assert post.categories[0] == news
|
|||||||
|
|
||||||
`get_or_create(_defaults: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[Model, bool]`
|
`get_or_create(_defaults: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[Model, bool]`
|
||||||
|
|
||||||
Tries to get a row meeting the criteria and if NoMatch exception is raised it creates a new one with given kwargs and _defaults.
|
Tries to get a row meeting the criteria and if `NoMatch` exception is raised it creates a new one with given kwargs and _defaults.
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
Read more in queries documentation [get_or_create][get_or_create]
|
Read more in queries documentation [get_or_create][get_or_create]
|
||||||
@ -127,7 +127,7 @@ provided Through model.
|
|||||||
|
|
||||||
Given sample like this:
|
Given sample like this:
|
||||||
|
|
||||||
```Python hl_lines="14-20 29"
|
```Python hl_lines="19-24 32"
|
||||||
--8<-- "../docs_src/relations/docs004.py"
|
--8<-- "../docs_src/relations/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ Updates the related model with provided keyword arguments, return number of upda
|
|||||||
Note that for `ManyToMany` relations update can also accept an argument with through field
|
Note that for `ManyToMany` relations update can also accept an argument with through field
|
||||||
name and a dictionary of fields.
|
name and a dictionary of fields.
|
||||||
|
|
||||||
```Python hl_lines="14-20 29"
|
```Python hl_lines="19-24 32"
|
||||||
--8<-- "../docs_src/relations/docs004.py"
|
--8<-- "../docs_src/relations/docs004.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
676
docs/releases.md
676
docs/releases.md
File diff suppressed because it is too large
Load Diff
@ -14,15 +14,15 @@ import sqlalchemy
|
|||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
|
||||||
metadata = sqlalchemy.MetaData()
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database("sqlite:///db.sqlite"),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -34,7 +34,7 @@ You can for example define a trigger that will set `album.is_best_seller` status
|
|||||||
|
|
||||||
Import `pre_update` decorator, for list of currently available decorators/ signals check below.
|
Import `pre_update` decorator, for list of currently available decorators/ signals check below.
|
||||||
|
|
||||||
```Python hl_lines="1"
|
```Python hl_lines="7"
|
||||||
--8<-- "../docs_src/signals/docs002.py"
|
--8<-- "../docs_src/signals/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ for which you want to run the signal receiver.
|
|||||||
|
|
||||||
Currently there is no way to set signal for all models at once without explicitly passing them all into registration of receiver.
|
Currently there is no way to set signal for all models at once without explicitly passing them all into registration of receiver.
|
||||||
|
|
||||||
```Python hl_lines="4-7"
|
```Python hl_lines="28-31"
|
||||||
--8<-- "../docs_src/signals/docs002.py"
|
--8<-- "../docs_src/signals/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ Currently there is no way to set signal for all models at once without explicitl
|
|||||||
Note that our newly created function has instance and class of the instance so you can easily run database
|
Note that our newly created function has instance and class of the instance so you can easily run database
|
||||||
queries inside your receivers if you want to.
|
queries inside your receivers if you want to.
|
||||||
|
|
||||||
```Python hl_lines="15-22"
|
```Python hl_lines="41-48"
|
||||||
--8<-- "../docs_src/signals/docs002.py"
|
--8<-- "../docs_src/signals/docs002.py"
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -75,15 +75,15 @@ You can define same receiver for multiple models at once by passing a list of mo
|
|||||||
# define a dummy debug function
|
# define a dummy debug function
|
||||||
@pre_update([Album, Track])
|
@pre_update([Album, Track])
|
||||||
async def before_update(sender, instance, **kwargs):
|
async def before_update(sender, instance, **kwargs):
|
||||||
print(f"{sender.get_name()}: {instance.json()}: {kwargs}")
|
print(f"{sender.get_name()}: {instance.model_dump_json()}: {kwargs}")
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course you can also create multiple functions for the same signal and model. Each of them will run at each signal.
|
Of course, you can also create multiple functions for the same signal and model. Each of them will run at each signal.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@pre_update(Album)
|
@pre_update(Album)
|
||||||
async def before_update(sender, instance, **kwargs):
|
async def before_update(sender, instance, **kwargs):
|
||||||
print(f"{sender.get_name()}: {instance.json()}: {kwargs}")
|
print(f"{sender.get_name()}: {instance.model_dump_json()}: {kwargs}")
|
||||||
|
|
||||||
@pre_update(Album)
|
@pre_update(Album)
|
||||||
async def before_update2(sender, instance, **kwargs):
|
async def before_update2(sender, instance, **kwargs):
|
||||||
@ -100,13 +100,13 @@ class AlbumAuditor:
|
|||||||
|
|
||||||
async def before_save(self, sender, instance, **kwargs):
|
async def before_save(self, sender, instance, **kwargs):
|
||||||
await AuditLog(
|
await AuditLog(
|
||||||
event_type=f"{self.event_type}_SAVE", event_log=instance.json()
|
event_type=f"{self.event_type}_SAVE", event_log=instance.model_dump_json()
|
||||||
).save()
|
).save()
|
||||||
|
|
||||||
auditor = AlbumAuditor()
|
auditor = AlbumAuditor()
|
||||||
pre_save(Album)(auditor.before_save)
|
pre_save(Album)(auditor.before_save)
|
||||||
# call above has same result like the one below
|
# call above has same result like the one below
|
||||||
Album.Meta.signals.pre_save.connect(auditor.before_save)
|
Album.ormar_config.signals.pre_save.connect(auditor.before_save)
|
||||||
# signals are also exposed on instance
|
# signals are also exposed on instance
|
||||||
album = Album(name='Miami')
|
album = Album(name='Miami')
|
||||||
album.signals.pre_save.connect(auditor.before_save)
|
album.signals.pre_save.connect(auditor.before_save)
|
||||||
@ -127,7 +127,7 @@ async def before_update(sender, instance, **kwargs):
|
|||||||
instance.is_best_seller = True
|
instance.is_best_seller = True
|
||||||
|
|
||||||
# disconnect given function from signal for given Model
|
# disconnect given function from signal for given Model
|
||||||
Album.Meta.signals.pre_save.disconnect(before_save)
|
Album.ormar_config.signals.pre_save.disconnect(before_save)
|
||||||
# signals are also exposed on instance
|
# signals are also exposed on instance
|
||||||
album = Album(name='Miami')
|
album = Album(name='Miami')
|
||||||
album.signals.pre_save.disconnect(before_save)
|
album.signals.pre_save.disconnect(before_save)
|
||||||
@ -142,7 +142,7 @@ album.signals.pre_save.disconnect(before_save)
|
|||||||
* bulk operations (`QuerySet.bulk_create` and `QuerySet.bulk_update`) as they are designed for speed.
|
* bulk operations (`QuerySet.bulk_create` and `QuerySet.bulk_update`) as they are designed for speed.
|
||||||
|
|
||||||
* queryset table level operations (`QuerySet.update` and `QuerySet.delete`) as they run on the underlying tables
|
* queryset table level operations (`QuerySet.update` and `QuerySet.delete`) as they run on the underlying tables
|
||||||
(more lak raw sql update/delete operations) and do not have specific instance.
|
(more like raw sql update/delete operations) and do not have specific instance.
|
||||||
|
|
||||||
### pre_save
|
### pre_save
|
||||||
|
|
||||||
@ -251,23 +251,23 @@ import sqlalchemy
|
|||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
|
||||||
metadata = sqlalchemy.MetaData()
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database("sqlite:///db.sqlite"),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "albums"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
is_best_seller: bool = ormar.Boolean(default=False)
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
play_count: int = ormar.Integer(default=0)
|
play_count: int = ormar.Integer(default=0)
|
||||||
|
|
||||||
Album.Meta.signals.your_custom_signal = ormar.Signal()
|
Album.ormar_config.signals.your_custom_signal = ormar.Signal()
|
||||||
Album.Meta.signals.your_custom_signal.connect(your_receiver_name)
|
Album.ormar_config.signals.your_custom_signal.connect(your_receiver_name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Actually under the hood signal is a `SignalEmitter` instance that keeps a dictionary of know signals, and allows you
|
Actually under the hood signal is a `SignalEmitter` instance that keeps a dictionary of know signals, and allows you
|
||||||
@ -276,13 +276,13 @@ to access them as attributes. When you try to access a signal that does not exis
|
|||||||
So example above can be simplified to. The `Signal` will be created for you.
|
So example above can be simplified to. The `Signal` will be created for you.
|
||||||
|
|
||||||
```
|
```
|
||||||
Album.Meta.signals.your_custom_signal.connect(your_receiver_name)
|
Album.ormar_config.signals.your_custom_signal.connect(your_receiver_name)
|
||||||
```
|
```
|
||||||
|
|
||||||
Now to trigger this signal you need to call send method of the Signal.
|
Now to trigger this signal you need to call send method of the Signal.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
await Album.Meta.signals.your_custom_signal.send(sender=Album)
|
await Album.ormar_config.signals.your_custom_signal.send(sender=Album)
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that sender is the only required parameter and it should be ormar Model class.
|
Note that sender is the only required parameter and it should be ormar Model class.
|
||||||
@ -290,6 +290,6 @@ Note that sender is the only required parameter and it should be ormar Model cla
|
|||||||
Additional parameters have to be passed as keyword arguments.
|
Additional parameters have to be passed as keyword arguments.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
await Album.Meta.signals.your_custom_signal.send(sender=Album, my_param=True)
|
await Album.ormar_config.signals.your_custom_signal.send(sender=Album, my_param=True)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -15,10 +15,10 @@ async with database.transaction():
|
|||||||
```
|
```
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
Note that it has to be the same `database` that the one used in Model's `Meta` class.
|
Note that it has to be the same `database` that the one used in Model's `ormar_config` object.
|
||||||
|
|
||||||
To avoid passing `database` instance around in your code you can extract the instance from each `Model`.
|
To avoid passing `database` instance around in your code you can extract the instance from each `Model`.
|
||||||
Database provided during declaration of `ormar.Model` is available through `Meta.database` and can
|
Database provided during declaration of `ormar.Model` is available through `ormar_config.database` and can
|
||||||
be reached from both class and instance.
|
be reached from both class and instance.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -26,24 +26,25 @@ import databases
|
|||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
import ormar
|
import ormar
|
||||||
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
database = databases.Database("sqlite:///")
|
base_ormar_config = OrmarConfig(
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
database = databases.Database("sqlite:///"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy()
|
||||||
database=database
|
|
||||||
metadata=metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=255)
|
name: str = ormar.String(max_length=255)
|
||||||
|
|
||||||
# database is accessible from class
|
# database is accessible from class
|
||||||
database = Author.Meta.database
|
database = Author.ormar_config.database
|
||||||
|
|
||||||
# as well as from instance
|
# as well as from instance
|
||||||
author = Author(name="Stephen King")
|
author = Author(name="Stephen King")
|
||||||
database = author.Meta.database
|
database = author.ormar_config.database
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use `.transaction()` as a function decorator on any async function:
|
You can also use `.transaction()` as a function decorator on any async function:
|
||||||
|
|||||||
@ -1,33 +1,32 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
from tests.settings import DATABASE_URL
|
from tests.settings import DATABASE_URL
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL)
|
database = databases.Database(DATABASE_URL)
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class BaseMeta(ormar.ModelMeta):
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy(tablename="authors", order_by=["-name"])
|
||||||
tablename = "authors"
|
|
||||||
order_by = ["-name"]
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
|
||||||
tablename = "books"
|
ormar_config = base_ormar_config.copy(
|
||||||
order_by = ["year", "-ranking"]
|
tablename="books", order_by=["year", "-ranking"]
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
|
|||||||
@ -1,46 +1,23 @@
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import databases
|
|
||||||
import sqlalchemy
|
|
||||||
from fastapi import FastAPI
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from tests.lifespan import lifespan
|
||||||
|
from tests.settings import create_config
|
||||||
|
|
||||||
app = FastAPI()
|
base_ormar_config = create_config()
|
||||||
metadata = sqlalchemy.MetaData()
|
app = FastAPI(lifespan=lifespan(base_ormar_config))
|
||||||
database = databases.Database("sqlite:///test.db")
|
|
||||||
app.state.database = database
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
|
||||||
async def startup() -> None:
|
|
||||||
database_ = app.state.database
|
|
||||||
if not database_.is_connected:
|
|
||||||
await database_.connect()
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("shutdown")
|
|
||||||
async def shutdown() -> None:
|
|
||||||
database_ = app.state.database
|
|
||||||
if database_.is_connected:
|
|
||||||
await database_.disconnect()
|
|
||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = base_ormar_config.copy(tablename="items")
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -68,7 +45,7 @@ async def create_category(category: Category):
|
|||||||
@app.put("/items/{item_id}")
|
@app.put("/items/{item_id}")
|
||||||
async def get_item(item_id: int, item: Item):
|
async def get_item(item_id: int, item: Item):
|
||||||
item_db = await Item.objects.get(pk=item_id)
|
item_db = await Item.objects.get(pk=item_id)
|
||||||
return await item_db.update(**item.dict())
|
return await item_db.update(**item.model_dump())
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/items/{item_id}")
|
@app.delete("/items/{item_id}")
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id = ormar.Integer(primary_key=True)
|
id = ormar.Integer(primary_key=True)
|
||||||
name = ormar.String(max_length=100)
|
name = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
|
import asyncio
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="departments")
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -29,12 +29,17 @@ class Course(ormar.Model):
|
|||||||
department: Optional[Department] = ormar.ForeignKey(Department)
|
department: Optional[Department] = ormar.ForeignKey(Department)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def verify():
|
||||||
department = await Department(name="Science").save()
|
department = await Department(name="Science").save()
|
||||||
course = Course(name="Math", completed=False, department=department)
|
course = Course(name="Math", completed=False, department=department)
|
||||||
|
|
||||||
print(department.courses[0])
|
print(department.courses[0])
|
||||||
# Will produce:
|
# Will produce:
|
||||||
# Course(id=None,
|
# Course(id=None,
|
||||||
# name='Math',
|
# name='Math',
|
||||||
# completed=False,
|
# completed=False,
|
||||||
# department=Department(id=None, name='Science'))
|
# department=Department(id=None, name='Science'))
|
||||||
|
await course.save()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(verify())
|
||||||
|
|||||||
@ -1,32 +1,32 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
import ormar
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
department: Optional[Department] = ormar.ForeignKey(Department, related_name="my_courses")
|
department: Optional[Department] = ormar.ForeignKey(
|
||||||
|
Department, related_name="my_courses"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
department = Department(name="Science")
|
department = Department(name="Science")
|
||||||
|
|||||||
@ -1,27 +1,28 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,20 +1,19 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from sqlalchemy import func, text
|
from sqlalchemy import func, text
|
||||||
|
|
||||||
import ormar
|
|
||||||
|
|
||||||
database = databases.Database("sqlite:///test.db")
|
database = databases.Database("sqlite:///test.db")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Product(ormar.Model):
|
class Product(ormar.Model):
|
||||||
class Meta:
|
|
||||||
tablename = "product"
|
ormar_config = ormar.OrmarConfig(
|
||||||
metadata = metadata
|
database=database, metadata=metadata, tablename="product"
|
||||||
database = database
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,19 +1,20 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
# if you omit this parameter it will be created automatically
|
# if you omit this parameter it will be created automatically
|
||||||
# as class.__name__.lower()+'s' -> "courses" in this example
|
# as class.__name__.lower()+'s' -> "courses" in this example
|
||||||
tablename = "my_courses"
|
tablename="my_courses",
|
||||||
database = database
|
)
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,34 +1,34 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
|
||||||
print(Course.__fields__)
|
print(Course.model_fields)
|
||||||
"""
|
"""
|
||||||
Will produce:
|
Will produce:
|
||||||
{'id': ModelField(name='id',
|
{'id': Field(name='id',
|
||||||
type=Optional[int],
|
type=Optional[int],
|
||||||
required=False,
|
required=False,
|
||||||
default=None),
|
default=None),
|
||||||
'name': ModelField(name='name',
|
'name': Field(name='name',
|
||||||
type=Optional[str],
|
type=Optional[str],
|
||||||
required=False,
|
required=False,
|
||||||
default=None),
|
default=None),
|
||||||
'completed': ModelField(name='completed',
|
'completed': Field(name='completed',
|
||||||
type=bool,
|
type=bool,
|
||||||
required=False,
|
required=False,
|
||||||
default=False)}
|
default=False)}
|
||||||
|
|||||||
@ -1,24 +1,28 @@
|
|||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
import ormar
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta(ormar.ModelMeta): # note you don't have to subclass - but it's recommended for ide completion and mypy
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
tablename="courses",
|
||||||
metadata = metadata
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
|
||||||
print(Course.Meta.table.columns)
|
print(Course.ormar_config.table.columns)
|
||||||
"""
|
"""
|
||||||
Will produce:
|
Will produce:
|
||||||
['courses.id', 'courses.name', 'courses.completed']
|
ImmutableColumnCollection(courses.id, courses.name, courses.completed)
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,67 +1,144 @@
|
|||||||
import databases
|
import pprint
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta(ormar.ModelMeta):
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
|
||||||
print({x: v.__dict__ for x, v in Course.Meta.model_fields.items()})
|
pprint.pp({x: v.__dict__ for x, v in Course.ormar_config.model_fields.items()})
|
||||||
"""
|
"""
|
||||||
Will produce:
|
Will produce:
|
||||||
{'completed': mappingproxy({'autoincrement': False,
|
{'id': {'__type__': <class 'int'>,
|
||||||
'choices': set(),
|
'__pydantic_type__': <class 'int'>,
|
||||||
'column_type': Boolean(),
|
'__sample__': 0,
|
||||||
'default': False,
|
'related_name': None,
|
||||||
'index': False,
|
|
||||||
'name': 'completed',
|
|
||||||
'nullable': True,
|
|
||||||
'primary_key': False,
|
|
||||||
'pydantic_only': False,
|
|
||||||
'server_default': None,
|
|
||||||
'unique': False}),
|
|
||||||
'id': mappingproxy({'autoincrement': True,
|
|
||||||
'choices': set(),
|
|
||||||
'column_type': Integer(),
|
'column_type': Integer(),
|
||||||
'default': None,
|
'constraints': [],
|
||||||
'ge': None,
|
|
||||||
'index': False,
|
|
||||||
'le': None,
|
|
||||||
'maximum': None,
|
|
||||||
'minimum': None,
|
|
||||||
'multiple_of': None,
|
|
||||||
'name': 'id',
|
'name': 'id',
|
||||||
'nullable': False,
|
'db_alias': None,
|
||||||
'primary_key': True,
|
'primary_key': True,
|
||||||
'pydantic_only': False,
|
'autoincrement': True,
|
||||||
'server_default': None,
|
'nullable': True,
|
||||||
'unique': False}),
|
'sql_nullable': False,
|
||||||
'name': mappingproxy({'allow_blank': False,
|
|
||||||
'autoincrement': False,
|
|
||||||
'choices': set(),
|
|
||||||
'column_type': String(max_length=100),
|
|
||||||
'curtail_length': None,
|
|
||||||
'default': None,
|
|
||||||
'index': False,
|
'index': False,
|
||||||
|
'unique': False,
|
||||||
|
'virtual': None,
|
||||||
|
'is_multi': None,
|
||||||
|
'is_relation': None,
|
||||||
|
'is_through': False,
|
||||||
|
'through_relation_name': None,
|
||||||
|
'through_reverse_relation_name': None,
|
||||||
|
'skip_reverse': False,
|
||||||
|
'skip_field': False,
|
||||||
|
'owner': <class '__main__.Course'>,
|
||||||
|
'to': None,
|
||||||
|
'to_pk_only': None,
|
||||||
|
'through': None,
|
||||||
|
'self_reference': False,
|
||||||
|
'self_reference_primary': None,
|
||||||
|
'orders_by': None,
|
||||||
|
'related_orders_by': None,
|
||||||
|
'encrypt_secret': None,
|
||||||
|
'encrypt_backend': <EncryptBackends.NONE: 0>,
|
||||||
|
'encrypt_custom_backend': None,
|
||||||
|
'ormar_default': None,
|
||||||
|
'server_default': None,
|
||||||
|
'comment': None,
|
||||||
|
'represent_as_base64_str': False,
|
||||||
|
'minimum': None,
|
||||||
|
'maximum': None,
|
||||||
|
'multiple_of': None,
|
||||||
|
'ge': None,
|
||||||
|
'le': None},
|
||||||
|
'name': {'__type__': <class 'str'>,
|
||||||
|
'__pydantic_type__': <class 'str'>,
|
||||||
|
'__sample__': 'string',
|
||||||
|
'related_name': None,
|
||||||
|
'column_type': String(length=100),
|
||||||
|
'constraints': [],
|
||||||
|
'name': 'name',
|
||||||
|
'db_alias': None,
|
||||||
|
'primary_key': False,
|
||||||
|
'autoincrement': False,
|
||||||
|
'nullable': False,
|
||||||
|
'sql_nullable': False,
|
||||||
|
'index': False,
|
||||||
|
'unique': False,
|
||||||
|
'virtual': None,
|
||||||
|
'is_multi': None,
|
||||||
|
'is_relation': None,
|
||||||
|
'is_through': False,
|
||||||
|
'through_relation_name': None,
|
||||||
|
'through_reverse_relation_name': None,
|
||||||
|
'skip_reverse': False,
|
||||||
|
'skip_field': False,
|
||||||
|
'owner': <class '__main__.Course'>,
|
||||||
|
'to': None,
|
||||||
|
'to_pk_only': None,
|
||||||
|
'through': None,
|
||||||
|
'self_reference': False,
|
||||||
|
'self_reference_primary': None,
|
||||||
|
'orders_by': None,
|
||||||
|
'related_orders_by': None,
|
||||||
|
'encrypt_secret': None,
|
||||||
|
'encrypt_backend': <EncryptBackends.NONE: 0>,
|
||||||
|
'encrypt_custom_backend': None,
|
||||||
|
'ormar_default': None,
|
||||||
|
'server_default': None,
|
||||||
|
'comment': None,
|
||||||
|
'represent_as_base64_str': False,
|
||||||
'max_length': 100,
|
'max_length': 100,
|
||||||
'min_length': None,
|
'min_length': None,
|
||||||
'name': 'name',
|
'regex': None},
|
||||||
'nullable': False,
|
'completed': {'__type__': <class 'bool'>,
|
||||||
|
'__pydantic_type__': <class 'bool'>,
|
||||||
|
'__sample__': True,
|
||||||
|
'related_name': None,
|
||||||
|
'column_type': Boolean(),
|
||||||
|
'constraints': [],
|
||||||
|
'name': 'completed',
|
||||||
|
'db_alias': None,
|
||||||
'primary_key': False,
|
'primary_key': False,
|
||||||
'pydantic_only': False,
|
'autoincrement': False,
|
||||||
'regex': None,
|
'nullable': True,
|
||||||
|
'sql_nullable': True,
|
||||||
|
'index': False,
|
||||||
|
'unique': False,
|
||||||
|
'virtual': None,
|
||||||
|
'is_multi': None,
|
||||||
|
'is_relation': None,
|
||||||
|
'is_through': False,
|
||||||
|
'through_relation_name': None,
|
||||||
|
'through_reverse_relation_name': None,
|
||||||
|
'skip_reverse': False,
|
||||||
|
'skip_field': False,
|
||||||
|
'owner': <class '__main__.Course'>,
|
||||||
|
'to': None,
|
||||||
|
'to_pk_only': None,
|
||||||
|
'through': None,
|
||||||
|
'self_reference': False,
|
||||||
|
'self_reference_primary': None,
|
||||||
|
'orders_by': None,
|
||||||
|
'related_orders_by': None,
|
||||||
|
'encrypt_secret': None,
|
||||||
|
'encrypt_backend': <EncryptBackends.NONE: 0>,
|
||||||
|
'encrypt_custom_backend': None,
|
||||||
|
'ormar_default': False,
|
||||||
'server_default': None,
|
'server_default': None,
|
||||||
'strip_whitespace': False,
|
'comment': None,
|
||||||
'unique': False})}
|
'represent_as_base64_str': False}}
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
# define your constraints in Meta class of the model
|
# define your constraints in OrmarConfig of the model
|
||||||
# it's a list that can contain multiple constraints
|
# it's a list that can contain multiple constraints
|
||||||
# hera a combination of name and column will have to be unique in db
|
# hera a combination of name and column will have to be unique in db
|
||||||
constraints = [ormar.UniqueColumns("name", "completed")]
|
constraints=[ormar.UniqueColumns("name", "completed")],
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,23 +1,31 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="courses")
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
course = Course(name="Painting for dummies", completed=False)
|
course = Course(name="Painting for dummies", completed=False)
|
||||||
await course.save()
|
await course.save()
|
||||||
|
|
||||||
await Course.objects.create(name="Painting for dummies", completed=False)
|
await Course.objects.create(name="Painting for dummies", completed=False)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
import ormar
|
DATABASE_URl = "sqlite:///test.db"
|
||||||
|
|
||||||
database = databases.Database("sqlite:///test.db", force_rollback=True)
|
database = databases.Database(DATABASE_URl, force_rollback=True)
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Child(ormar.Model):
|
class Child(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "children"
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
tablename="children",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(name="child_id", primary_key=True)
|
id: int = ormar.Integer(name="child_id", primary_key=True)
|
||||||
first_name: str = ormar.String(name="fname", max_length=100)
|
first_name: str = ormar.String(name="fname", max_length=100)
|
||||||
|
|||||||
@ -1,20 +1,33 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from .docs010 import Artist # previous example
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///test.db", force_rollback=True)
|
database = databases.Database("sqlite:///test.db", force_rollback=True)
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
|
class Artist(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="artists",
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(name="artist_id", primary_key=True)
|
||||||
|
first_name: str = ormar.String(name="fname", max_length=100)
|
||||||
|
last_name: str = ormar.String(name="lname", max_length=100)
|
||||||
|
born_year: int = ormar.Integer(name="year")
|
||||||
|
|
||||||
|
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
|
||||||
tablename = "music_albums"
|
ormar_config = ormar.OrmarConfig(
|
||||||
metadata = metadata
|
database=database,
|
||||||
database = database
|
metadata=metadata,
|
||||||
|
tablename="music_albums",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(name="album_id", primary_key=True)
|
id: int = ormar.Integer(name="album_id", primary_key=True)
|
||||||
name: str = ormar.String(name="album_name", max_length=100)
|
name: str = ormar.String(name="album_name", max_length=100)
|
||||||
|
|||||||
@ -1,25 +1,40 @@
|
|||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
import ormar
|
DATABASE_URl = "sqlite:///test.db"
|
||||||
from .docs008 import Child
|
|
||||||
|
|
||||||
database = databases.Database("sqlite:///test.db", force_rollback=True)
|
database = databases.Database(DATABASE_URl, force_rollback=True)
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
|
class Child(ormar.Model):
|
||||||
|
ormar_config = ormar.OrmarConfig(
|
||||||
|
database=database,
|
||||||
|
metadata=metadata,
|
||||||
|
tablename="children",
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(name="child_id", primary_key=True)
|
||||||
|
first_name: str = ormar.String(name="fname", max_length=100)
|
||||||
|
last_name: str = ormar.String(name="lname", max_length=100)
|
||||||
|
born_year: int = ormar.Integer(name="year_born", nullable=True)
|
||||||
|
|
||||||
|
|
||||||
class ArtistChildren(ormar.Model):
|
class ArtistChildren(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "children_x_artists"
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
tablename="children_x_artists",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Artist(ormar.Model):
|
class Artist(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "artists"
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
tablename="artists",
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(name="artist_id", primary_key=True)
|
id: int = ormar.Integer(name="artist_id", primary_key=True)
|
||||||
first_name: str = ormar.String(name="fname", max_length=100)
|
first_name: str = ormar.String(name="fname", max_length=100)
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import databases
|
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
|
||||||
class Meta:
|
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: ormar.Integer(primary_key=True)
|
|
||||||
name: ormar.String(max_length=100)
|
|
||||||
completed: ormar.Boolean(default=False)
|
|
||||||
|
|
||||||
c1 = Course()
|
|
||||||
@ -1,16 +1,16 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id = ormar.Integer(primary_key=True)
|
id = ormar.Integer(primary_key=True)
|
||||||
name = ormar.String(max_length=100)
|
name = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,27 +1,22 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
import ormar
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
database = databases.Database("sqlite:///test.db", force_rollback=True)
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
# note that you do not have to subclass ModelMeta,
|
|
||||||
# it's useful for type hints and code completion
|
|
||||||
class MainMeta(ormar.ModelMeta):
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
|
|
||||||
class Artist(ormar.Model):
|
class Artist(ormar.Model):
|
||||||
class Meta(MainMeta):
|
|
||||||
# note that tablename is optional
|
# note that tablename is optional
|
||||||
# if not provided ormar will user class.__name__.lower()+'s'
|
# if not provided ormar will user class.__name__.lower()+'s'
|
||||||
# -> artists in this example
|
# -> artists in this example
|
||||||
pass
|
ormar_config = ormar_base_config.copy()
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
first_name: str = ormar.String(max_length=100)
|
first_name: str = ormar.String(max_length=100)
|
||||||
@ -30,8 +25,7 @@ class Artist(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta(MainMeta):
|
ormar_config = ormar_base_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,18 +1,19 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import pydantic
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
non_db_field: str = ormar.String(max_length=100, pydantic_only=True)
|
non_db_field: str = pydantic.Field(max_length=100)
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from ormar import property_field
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
completed: bool = ormar.Boolean(default=False)
|
completed: bool = ormar.Boolean(default=False)
|
||||||
|
|
||||||
@property_field
|
@property
|
||||||
def prefixed_name(self):
|
def prefixed_name(self):
|
||||||
return "custom_prefix__" + self.name
|
return "custom_prefix__" + self.name
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import pydantic
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
class Config:
|
model_config = pydantic.ConfigDict(frozen=True)
|
||||||
allow_mutation = False
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
# define your constraints in Meta class of the model
|
# define your constraints in OrmarConfig of the model
|
||||||
# it's a list that can contain multiple constraints
|
# it's a list that can contain multiple constraints
|
||||||
# hera a combination of name and column will have a compound index in the db
|
# hera a combination of name and column will have a compound index in the db
|
||||||
constraints = [ormar.IndexColumns("name", "completed")]
|
constraints=[ormar.IndexColumns("name", "completed")],
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,23 +1,24 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import databases
|
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
# define your constraints in Meta class of the model
|
# define your constraints in OrmarConfig of the model
|
||||||
# it's a list that can contain multiple constraints
|
# it's a list that can contain multiple constraints
|
||||||
# hera a combination of name and column will have a level check in the db
|
# hera a combination of name and column will have a level check in the db
|
||||||
constraints=[
|
constraints=[
|
||||||
ormar.CheckColumns("start_time < end_time", name="date_check"),
|
ormar.CheckColumns("start_time < end_time", name="date_check"),
|
||||||
]
|
],
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -4,25 +4,23 @@ import databases
|
|||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Album(ormar.Model):
|
class Album(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="album")
|
||||||
tablename = "album"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Track(ormar.Model):
|
class Track(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="track")
|
||||||
tablename = "track"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
album: Optional[Album] = ormar.ForeignKey(Album)
|
album: Optional[Album] = ormar.ForeignKey(Album)
|
||||||
|
|||||||
@ -1,28 +1,47 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(
|
||||||
tablename = "books"
|
tablename="books",
|
||||||
metadata = metadata
|
)
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
author: str = ormar.String(max_length=100)
|
author: str = ormar.String(max_length=100)
|
||||||
genre: str = ormar.String(max_length=100, default='Fiction',
|
genre: str = ormar.String(
|
||||||
choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
max_length=100,
|
||||||
|
default="Fiction",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
await Book.objects.create(title='Tom Sawyer', author="Twain, Mark", genre='Adventure')
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
await Book.objects.create(title='War and Peace', author="Tolstoy, Leo", genre='Fiction')
|
async def run_query():
|
||||||
await Book.objects.create(title='Anna Karenina', author="Tolstoy, Leo", genre='Fiction')
|
await Book.objects.create(
|
||||||
|
title="Tom Sawyer", author="Twain, Mark", genre="Adventure"
|
||||||
|
)
|
||||||
|
await Book.objects.create(
|
||||||
|
title="War and Peace", author="Tolstoy, Leo", genre="Fiction"
|
||||||
|
)
|
||||||
|
await Book.objects.create(
|
||||||
|
title="Anna Karenina", author="Tolstoy, Leo", genre="Fiction"
|
||||||
|
)
|
||||||
|
|
||||||
await Book.objects.update(each=True, genre='Fiction')
|
await Book.objects.update(each=True, genre="Fiction")
|
||||||
all_books = await Book.objects.filter(genre='Fiction').all()
|
all_books = await Book.objects.filter(genre="Fiction").all()
|
||||||
assert len(all_books) == 3
|
assert len(all_books) == 3
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,32 +1,51 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy()
|
||||||
tablename = "books"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
author: str = ormar.String(max_length=100)
|
author: str = ormar.String(max_length=100)
|
||||||
genre: str = ormar.String(max_length=100, default='Fiction',
|
genre: str = ormar.String(
|
||||||
choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
max_length=100,
|
||||||
|
default="Fiction",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
await Book.objects.create(title='Tom Sawyer', author="Twain, Mark", genre='Adventure')
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
await Book.objects.create(title='War and Peace', author="Tolstoy, Leo", genre='Fiction')
|
async def run_query():
|
||||||
await Book.objects.create(title='Anna Karenina', author="Tolstoy, Leo", genre='Fiction')
|
await Book.objects.create(
|
||||||
|
title="Tom Sawyer", author="Twain, Mark", genre="Adventure"
|
||||||
|
)
|
||||||
|
await Book.objects.create(
|
||||||
|
title="War and Peace", author="Tolstoy, Leo", genre="Fiction"
|
||||||
|
)
|
||||||
|
await Book.objects.create(
|
||||||
|
title="Anna Karenina", author="Tolstoy, Leo", genre="Fiction"
|
||||||
|
)
|
||||||
|
|
||||||
# if not exist the instance will be persisted in db
|
# if not exist the instance will be persisted in db
|
||||||
vol2 = await Book.objects.update_or_create(title="Volume II", author='Anonymous', genre='Fiction')
|
vol2 = await Book.objects.update_or_create(
|
||||||
assert await Book.objects.count() == 1
|
title="Volume II", author="Anonymous", genre="Fiction"
|
||||||
|
)
|
||||||
|
assert await Book.objects.count() == 4
|
||||||
|
|
||||||
# if pk or pkname passed in kwargs (like id here) the object will be updated
|
# if pk or pkname passed in kwargs (like id here) the object will be updated
|
||||||
assert await Book.objects.update_or_create(id=vol2.id, genre='Historic')
|
assert await Book.objects.update_or_create(id=vol2.id, genre="Historic")
|
||||||
assert await Book.objects.count() == 1
|
assert await Book.objects.count() == 4
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,22 +1,28 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ToDo(ormar.Model):
|
class ToDo(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="todos")
|
||||||
tablename = "todos"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
text: str = ormar.String(max_length=500)
|
text: str = ormar.String(max_length=500)
|
||||||
completed = ormar.Boolean(default=False)
|
completed = ormar.Boolean(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
# create multiple instances at once with bulk_create
|
# create multiple instances at once with bulk_create
|
||||||
await ToDo.objects.bulk_create(
|
await ToDo.objects.bulk_create(
|
||||||
[
|
[
|
||||||
@ -28,3 +34,6 @@ await ToDo.objects.bulk_create(
|
|||||||
|
|
||||||
todoes = await ToDo.objects.all()
|
todoes = await ToDo.objects.all()
|
||||||
assert len(todoes) == 3
|
assert len(todoes) == 3
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,30 +1,47 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="books")
|
||||||
tablename = "books"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
author: str = ormar.String(max_length=100)
|
author: str = ormar.String(max_length=100)
|
||||||
genre: str = ormar.String(max_length=100, default='Fiction',
|
genre: str = ormar.String(
|
||||||
choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
max_length=100,
|
||||||
|
default="Fiction",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
await Book.objects.create(title='Tom Sawyer', author="Twain, Mark", genre='Adventure')
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
await Book.objects.create(title='War and Peace in Space', author="Tolstoy, Leo", genre='Fantasy')
|
async def run_query():
|
||||||
await Book.objects.create(title='Anna Karenina', author="Tolstoy, Leo", genre='Fiction')
|
await Book.objects.create(
|
||||||
|
title="Tom Sawyer", author="Twain, Mark", genre="Adventure"
|
||||||
|
)
|
||||||
|
await Book.objects.create(
|
||||||
|
title="War and Peace in Space", author="Tolstoy, Leo", genre="Fantasy"
|
||||||
|
)
|
||||||
|
await Book.objects.create(
|
||||||
|
title="Anna Karenina", author="Tolstoy, Leo", genre="Fiction"
|
||||||
|
)
|
||||||
|
|
||||||
# delete accepts kwargs that will be used in filter
|
# delete accepts kwargs that will be used in filter
|
||||||
# acting in same way as queryset.filter(**kwargs).delete()
|
# acting in same way as queryset.filter(**kwargs).delete()
|
||||||
await Book.objects.delete(genre='Fantasy') # delete all fantasy books
|
await Book.objects.delete(genre="Fantasy") # delete all fantasy books
|
||||||
all_books = await Book.objects.all()
|
all_books = await Book.objects.all()
|
||||||
assert len(all_books) == 2
|
assert len(all_books) == 2
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from tests.settings import DATABASE_URL
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Company(ormar.Model):
|
class Company(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="companies")
|
||||||
tablename = "companies"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -20,10 +22,7 @@ class Company(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Car(ormar.Model):
|
class Car(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="cars")
|
||||||
tablename = "cars"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
manufacturer = ormar.ForeignKey(Company)
|
manufacturer = ormar.ForeignKey(Company)
|
||||||
@ -34,12 +33,34 @@ class Car(ormar.Model):
|
|||||||
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
# build some sample data
|
# build some sample data
|
||||||
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
||||||
await Car.objects.create(manufacturer=toyota, name="Corolla", year=2020, gearbox_type='Manual', gears=5,
|
await Car.objects.create(
|
||||||
aircon_type='Manual')
|
manufacturer=toyota,
|
||||||
await Car.objects.create(manufacturer=toyota, name="Yaris", year=2019, gearbox_type='Manual', gears=5,
|
name="Corolla",
|
||||||
aircon_type='Manual')
|
year=2020,
|
||||||
await Car.objects.create(manufacturer=toyota, name="Supreme", year=2020, gearbox_type='Auto', gears=6,
|
gearbox_type="Manual",
|
||||||
aircon_type='Auto')
|
gears=5,
|
||||||
|
aircon_type="Manual",
|
||||||
|
)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Yaris",
|
||||||
|
year=2019,
|
||||||
|
gearbox_type="Manual",
|
||||||
|
gears=5,
|
||||||
|
aircon_type="Manual",
|
||||||
|
)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Supreme",
|
||||||
|
year=2020,
|
||||||
|
gearbox_type="Auto",
|
||||||
|
gears=6,
|
||||||
|
aircon_type="Auto",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,34 +1,35 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from tests.settings import DATABASE_URL
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Owner(ormar.Model):
|
class Owner(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="owners")
|
||||||
tablename = "owners"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Toy(ormar.Model):
|
class Toy(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="toys")
|
||||||
tablename = "toys"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
owner: Owner = ormar.ForeignKey(Owner)
|
owner: Owner = ormar.ForeignKey(Owner)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
# build some sample data
|
# build some sample data
|
||||||
aphrodite = await Owner.objects.create(name="Aphrodite")
|
aphrodite = await Owner.objects.create(name="Aphrodite")
|
||||||
hermes = await Owner.objects.create(name="Hermes")
|
hermes = await Owner.objects.create(name="Hermes")
|
||||||
@ -40,3 +41,6 @@ await Toy.objects.create(name="Toy 2", owner=aphrodite)
|
|||||||
await Toy.objects.create(name="Toy 1", owner=zeus)
|
await Toy.objects.create(name="Toy 1", owner=zeus)
|
||||||
await Toy.objects.create(name="Toy 3", owner=aphrodite)
|
await Toy.objects.create(name="Toy 3", owner=aphrodite)
|
||||||
await Toy.objects.create(name="Toy 6", owner=hermes)
|
await Toy.objects.create(name="Toy 6", owner=hermes)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,18 +1,21 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from tests.settings import DATABASE_URL
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
database = databases.Database(DATABASE_URL, force_rollback=True)
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Company(ormar.Model):
|
class Company(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="companies")
|
||||||
tablename = "companies"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -20,10 +23,7 @@ class Company(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Car(ormar.Model):
|
class Car(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="cars")
|
||||||
tablename = "cars"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
manufacturer = ormar.ForeignKey(Company)
|
manufacturer = ormar.ForeignKey(Company)
|
||||||
@ -34,35 +34,81 @@ class Car(ormar.Model):
|
|||||||
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
# build some sample data
|
# build some sample data
|
||||||
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
||||||
await Car.objects.create(manufacturer=toyota, name="Corolla", year=2020, gearbox_type='Manual', gears=5,
|
await Car.objects.create(
|
||||||
aircon_type='Manual')
|
manufacturer=toyota,
|
||||||
await Car.objects.create(manufacturer=toyota, name="Yaris", year=2019, gearbox_type='Manual', gears=5,
|
name="Corolla",
|
||||||
aircon_type='Manual')
|
year=2020,
|
||||||
await Car.objects.create(manufacturer=toyota, name="Supreme", year=2020, gearbox_type='Auto', gears=6,
|
gearbox_type="Manual",
|
||||||
aircon_type='Auto')
|
gears=5,
|
||||||
|
aircon_type="Manual",
|
||||||
|
)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Yaris",
|
||||||
|
year=2019,
|
||||||
|
gearbox_type="Manual",
|
||||||
|
gears=5,
|
||||||
|
aircon_type="Manual",
|
||||||
|
)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Supreme",
|
||||||
|
year=2020,
|
||||||
|
gearbox_type="Auto",
|
||||||
|
gears=6,
|
||||||
|
aircon_type="Auto",
|
||||||
|
)
|
||||||
|
|
||||||
# select manufacturer but only name - to include related models use notation {model_name}__{column}
|
# select manufacturer but only name,
|
||||||
all_cars = await Car.objects.select_related('manufacturer').exclude_fields(
|
# to include related models use notation {model_name}__{column}
|
||||||
['year', 'gearbox_type', 'gears', 'aircon_type', 'company__founded']).all()
|
all_cars = (
|
||||||
|
await Car.objects.select_related("manufacturer")
|
||||||
|
.exclude_fields(
|
||||||
|
["year", "gearbox_type", "gears", "aircon_type", "manufacturer__founded"]
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
for car in all_cars:
|
for car in all_cars:
|
||||||
# excluded columns will yield None
|
# excluded columns will yield None
|
||||||
assert all(getattr(car, x) is None for x in ['year', 'gearbox_type', 'gears', 'aircon_type'])
|
assert all(
|
||||||
# included column on related models will be available, pk column is always included
|
getattr(car, x) is None
|
||||||
|
for x in ["year", "gearbox_type", "gears", "aircon_type"]
|
||||||
|
)
|
||||||
|
# included column on related models will be available,
|
||||||
|
# pk column is always included
|
||||||
# even if you do not include it in fields list
|
# even if you do not include it in fields list
|
||||||
assert car.manufacturer.name == 'Toyota'
|
assert car.manufacturer.name == "Toyota"
|
||||||
# also in the nested related models - you cannot exclude pk - it's always auto added
|
# also in the nested related models -
|
||||||
|
# you cannot exclude pk - it's always auto added
|
||||||
assert car.manufacturer.founded is None
|
assert car.manufacturer.founded is None
|
||||||
|
|
||||||
# fields() can be called several times, building up the columns to select
|
# fields() can be called several times,
|
||||||
# models selected in select_related but with no columns in fields list implies all fields
|
# building up the columns to select,
|
||||||
all_cars = await Car.objects.select_related('manufacturer').exclude_fields('year').exclude_fields(
|
# models selected in select_related
|
||||||
['gear', 'gearbox_type']).all()
|
# but with no columns in fields list implies all fields
|
||||||
# all fields from company model are selected
|
all_cars = (
|
||||||
assert all_cars[0].manufacturer.name == 'Toyota'
|
await Car.objects.select_related("manufacturer")
|
||||||
|
.exclude_fields("year")
|
||||||
|
.exclude_fields(["gear", "gearbox_type"])
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
# all fiels from company model are selected
|
||||||
|
assert all_cars[0].manufacturer.name == "Toyota"
|
||||||
assert all_cars[0].manufacturer.founded == 1937
|
assert all_cars[0].manufacturer.founded == 1937
|
||||||
|
|
||||||
# cannot exclude mandatory model columns - company__name in this example - note usage of dict/set this time
|
# cannot exclude mandatory model columns -
|
||||||
await Car.objects.select_related('manufacturer').exclude_fields([{'company': {'name'}}]).all()
|
# manufacturer__name in this example - note usage of dict/set this time
|
||||||
|
try:
|
||||||
|
await Car.objects.select_related("manufacturer").exclude_fields(
|
||||||
|
{"manufacturer": {"name"}}
|
||||||
|
).all()
|
||||||
|
except ValidationError:
|
||||||
# will raise pydantic ValidationError as company.name is required
|
# will raise pydantic ValidationError as company.name is required
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,33 +1,71 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
|
||||||
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Company(ormar.Model):
|
||||||
|
ormar_config = ormar_base_config.copy(tablename="companies")
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
founded: int = ormar.Integer(nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Car(ormar.Model):
|
||||||
|
ormar_config = ormar_base_config.copy(tablename="cars")
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
manufacturer = ormar.ForeignKey(Company)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
year: int = ormar.Integer(nullable=True)
|
||||||
|
gearbox_type: str = ormar.String(max_length=20, nullable=True)
|
||||||
|
gears: int = ormar.Integer(nullable=True)
|
||||||
|
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
# 1. like in example above
|
# 1. like in example above
|
||||||
await Car.objects.select_related('manufacturer').fields(['id', 'name', 'manufacturer__name']).all()
|
await Car.objects.select_related("manufacturer").fields(
|
||||||
|
["id", "name", "manufacturer__name"]
|
||||||
|
).all()
|
||||||
|
|
||||||
# 2. to mark a field as required use ellipsis
|
# 2. to mark a field as required use ellipsis
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
await Car.objects.select_related("manufacturer").fields(
|
||||||
'name': ...,
|
{"id": ..., "name": ..., "manufacturer": {"name": ...}}
|
||||||
'manufacturer': {
|
).all()
|
||||||
'name': ...}
|
|
||||||
}).all()
|
|
||||||
|
|
||||||
# 3. to include whole nested model use ellipsis
|
# 3. to include whole nested model use ellipsis
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
await Car.objects.select_related("manufacturer").fields(
|
||||||
'name': ...,
|
{"id": ..., "name": ..., "manufacturer": ...}
|
||||||
'manufacturer': ...
|
).all()
|
||||||
}).all()
|
|
||||||
|
|
||||||
# 4. to specify fields at last nesting level you can also use set - equivalent to 2. above
|
# 4. to specify fields at last nesting level you can also use set
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
# - equivalent to 2. above
|
||||||
'name': ...,
|
await Car.objects.select_related("manufacturer").fields(
|
||||||
'manufacturer': {'name'}
|
{"id": ..., "name": ..., "manufacturer": {"name"}}
|
||||||
}).all()
|
).all()
|
||||||
|
|
||||||
# 5. of course set can have multiple fields
|
# 5. of course set can have multiple fields
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
await Car.objects.select_related("manufacturer").fields(
|
||||||
'name': ...,
|
{"id": ..., "name": ..., "manufacturer": {"name", "founded"}}
|
||||||
'manufacturer': {'name', 'founded'}
|
).all()
|
||||||
}).all()
|
|
||||||
|
|
||||||
# 6. you can include all nested fields but it will be equivalent of 3. above which is shorter
|
# 6. you can include all nested fields,
|
||||||
await Car.objects.select_related('manufacturer').fields({'id': ...,
|
# but it will be equivalent of 3. above which is shorter
|
||||||
'name': ...,
|
await Car.objects.select_related("manufacturer").fields(
|
||||||
'manufacturer': {'id', 'name', 'founded'}
|
{"id": ..., "name": ..., "manufacturer": {"id", "name", "founded"}}
|
||||||
}).all()
|
).all()
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
@ -1,27 +1,28 @@
|
|||||||
from typing import Optional, Dict, Union
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
database = databases.Database("sqlite:///db.sqlite")
|
||||||
metadata = sqlalchemy.MetaData()
|
metadata = sqlalchemy.MetaData()
|
||||||
|
|
||||||
|
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
database = database
|
database=database,
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
|
)
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -38,7 +39,7 @@ course = Course(name="Math", completed=False, department=department)
|
|||||||
course2 = Course(name="Math II", completed=False, department=department.pk)
|
course2 = Course(name="Math II", completed=False, department=department.pk)
|
||||||
|
|
||||||
# set up a relation with dictionary corresponding to related model
|
# set up a relation with dictionary corresponding to related model
|
||||||
course3 = Course(name="Math III", completed=False, department=department.dict())
|
course3 = Course(name="Math III", completed=False, department=department.model_dump())
|
||||||
|
|
||||||
# explicitly set up None
|
# explicitly set up None
|
||||||
course4 = Course(name="Math III", completed=False, department=None)
|
course4 = Course(name="Math III", completed=False, department=None)
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
from typing import Optional, Union, List
|
from typing import List, Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import ormar
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
database = databases.Database("sqlite:///db.sqlite")
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="authors")
|
||||||
tablename = "authors"
|
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
first_name: str = ormar.String(max_length=80)
|
first_name: str = ormar.String(max_length=80)
|
||||||
@ -20,20 +20,14 @@ class Author(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=40)
|
name: str = ormar.String(max_length=40)
|
||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="posts")
|
||||||
tablename = "posts"
|
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
|
|||||||
@ -1,16 +1,25 @@
|
|||||||
|
from typing import Dict, Optional, Union
|
||||||
|
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Department(ormar.Model):
|
class Department(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Course(ormar.Model):
|
class Course(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy()
|
||||||
database = database
|
|
||||||
metadata = metadata
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|||||||
@ -1,19 +1,23 @@
|
|||||||
class BaseMeta(ormar.ModelMeta):
|
import databases
|
||||||
database = database
|
import ormar
|
||||||
metadata = metadata
|
import sqlalchemy
|
||||||
|
|
||||||
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = ormar_base_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
|
|
||||||
id = ormar.Integer(primary_key=True)
|
id = ormar.Integer(primary_key=True)
|
||||||
name = ormar.String(max_length=40)
|
name = ormar.String(max_length=40)
|
||||||
|
|
||||||
|
|
||||||
class PostCategory(ormar.Model):
|
class PostCategory(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = ormar_base_config.copy(tablename="posts_x_categories")
|
||||||
tablename = "posts_x_categories"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
sort_order: int = ormar.Integer(nullable=True)
|
sort_order: int = ormar.Integer(nullable=True)
|
||||||
@ -21,8 +25,7 @@ class PostCategory(ormar.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Post(ormar.Model):
|
class Post(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = ormar_base_config.copy()
|
||||||
pass
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
title: str = ormar.String(max_length=200)
|
title: str = ormar.String(max_length=200)
|
||||||
|
|||||||
0
docs_src/select_columns/__init__.py
Normal file
0
docs_src/select_columns/__init__.py
Normal file
65
docs_src/select_columns/docs001.py
Normal file
65
docs_src/select_columns/docs001.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
|
from tests.settings import DATABASE_URL
|
||||||
|
|
||||||
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL, force_rollback=True),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Company(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy(tablename="companies")
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
founded: int = ormar.Integer(nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Car(ormar.Model):
|
||||||
|
ormar_config = base_ormar_config.copy()
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
manufacturer = ormar.ForeignKey(Company)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
year: int = ormar.Integer(nullable=True)
|
||||||
|
gearbox_type: str = ormar.String(max_length=20, nullable=True)
|
||||||
|
gears: int = ormar.Integer(nullable=True)
|
||||||
|
aircon_type: str = ormar.String(max_length=20, nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=base_ormar_config)
|
||||||
|
async def sample_data():
|
||||||
|
# build some sample data
|
||||||
|
toyota = await Company.objects.create(name="Toyota", founded=1937)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Corolla",
|
||||||
|
year=2020,
|
||||||
|
gearbox_type="Manual",
|
||||||
|
gears=5,
|
||||||
|
aircon_type="Manual",
|
||||||
|
)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Yaris",
|
||||||
|
year=2019,
|
||||||
|
gearbox_type="Manual",
|
||||||
|
gears=5,
|
||||||
|
aircon_type="Manual",
|
||||||
|
)
|
||||||
|
await Car.objects.create(
|
||||||
|
manufacturer=toyota,
|
||||||
|
name="Supreme",
|
||||||
|
year=2020,
|
||||||
|
gearbox_type="Auto",
|
||||||
|
gears=6,
|
||||||
|
aircon_type="Auto",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(sample_data())
|
||||||
@ -1,5 +1,29 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
import databases
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
from examples import create_drop_database
|
||||||
from ormar import pre_update
|
from ormar import pre_update
|
||||||
|
|
||||||
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
|
database=databases.Database(DATABASE_URL),
|
||||||
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Album(ormar.Model):
|
||||||
|
ormar_config = ormar_base_config.copy(
|
||||||
|
tablename="albums",
|
||||||
|
)
|
||||||
|
|
||||||
|
id: int = ormar.Integer(primary_key=True)
|
||||||
|
name: str = ormar.String(max_length=100)
|
||||||
|
is_best_seller: bool = ormar.Boolean(default=False)
|
||||||
|
play_count: int = ormar.Integer(default=0)
|
||||||
|
|
||||||
|
|
||||||
@pre_update(Album)
|
@pre_update(Album)
|
||||||
async def before_update(sender, instance, **kwargs):
|
async def before_update(sender, instance, **kwargs):
|
||||||
@ -7,6 +31,8 @@ async def before_update(sender, instance, **kwargs):
|
|||||||
instance.is_best_seller = True
|
instance.is_best_seller = True
|
||||||
|
|
||||||
|
|
||||||
|
@create_drop_database(base_config=ormar_base_config)
|
||||||
|
async def run_query():
|
||||||
# here album.play_count ans is_best_seller get default values
|
# here album.play_count ans is_best_seller get default values
|
||||||
album = await Album.objects.create(name="Venice")
|
album = await Album.objects.create(name="Venice")
|
||||||
assert not album.is_best_seller
|
assert not album.is_best_seller
|
||||||
@ -20,3 +46,6 @@ assert not album.is_best_seller
|
|||||||
album.play_count = 60
|
album.play_count = 60
|
||||||
await album.update()
|
await album.update()
|
||||||
assert album.is_best_seller
|
assert album.is_best_seller
|
||||||
|
|
||||||
|
|
||||||
|
asyncio.run(run_query())
|
||||||
|
|||||||
20
docs_src/test_all_docs.py
Normal file
20
docs_src/test_all_docs.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
filepaths = []
|
||||||
|
path = Path(__file__).parent
|
||||||
|
for p in path.rglob("*"):
|
||||||
|
print(p.name)
|
||||||
|
for p in path.rglob("*"):
|
||||||
|
if p.name.endswith(".py") and not p.name == "__init__.py" and p != Path(__file__):
|
||||||
|
filepath_ = str(p.resolve())
|
||||||
|
filepaths.append(filepath_)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("filepath", filepaths)
|
||||||
|
def test_all_docs(filepath: str):
|
||||||
|
result = subprocess.run([sys.executable, filepath])
|
||||||
|
assert result.returncode == 0
|
||||||
3
examples/__init__.py
Normal file
3
examples/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from .utils import create_drop_database
|
||||||
|
|
||||||
|
__all__ = ["create_drop_database"]
|
||||||
@ -1,47 +1,45 @@
|
|||||||
|
from contextlib import asynccontextmanager
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
|
import ormar
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
import ormar
|
DATABASE_URL = "sqlite:///test.db"
|
||||||
|
|
||||||
app = FastAPI()
|
ormar_base_config = ormar.OrmarConfig(
|
||||||
metadata = sqlalchemy.MetaData()
|
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
|
||||||
database = databases.Database("sqlite:///test.db")
|
)
|
||||||
app.state.database = database
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_event("startup")
|
@asynccontextmanager
|
||||||
async def startup() -> None:
|
async def lifespan(app: FastAPI):
|
||||||
database_ = app.state.database
|
database_ = app.state.database
|
||||||
if not database_.is_connected:
|
if not database_.is_connected:
|
||||||
await database_.connect()
|
await database_.connect()
|
||||||
|
yield
|
||||||
|
|
||||||
@app.on_event("shutdown")
|
|
||||||
async def shutdown() -> None:
|
|
||||||
database_ = app.state.database
|
database_ = app.state.database
|
||||||
if database_.is_connected:
|
if database_.is_connected:
|
||||||
await database_.disconnect()
|
await database_.disconnect()
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
metadata = sqlalchemy.MetaData()
|
||||||
|
database = databases.Database("sqlite:///test.db")
|
||||||
|
app.state.database = database
|
||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="categories")
|
||||||
tablename = "categories"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Item(ormar.Model):
|
class Item(ormar.Model):
|
||||||
class Meta:
|
ormar_config = ormar_base_config.copy(tablename="items")
|
||||||
tablename = "items"
|
|
||||||
metadata = metadata
|
|
||||||
database = database
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
@ -69,7 +67,7 @@ async def create_category(category: Category):
|
|||||||
@app.put("/items/{item_id}")
|
@app.put("/items/{item_id}")
|
||||||
async def get_item(item_id: int, item: Item):
|
async def get_item(item_id: int, item: Item):
|
||||||
item_db = await Item.objects.get(pk=item_id)
|
item_db = await Item.objects.get(pk=item_id)
|
||||||
return await item_db.update(**item.dict())
|
return await item_db.update(**item.model_dump())
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/items/{item_id}")
|
@app.delete("/items/{item_id}")
|
||||||
|
|||||||
@ -1,45 +1,40 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import pydantic
|
|
||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
|
import pydantic
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
DATABASE_URL = "sqlite:///db.sqlite"
|
DATABASE_URL = "sqlite:///db.sqlite"
|
||||||
database = databases.Database(DATABASE_URL)
|
|
||||||
metadata = sqlalchemy.MetaData()
|
|
||||||
|
|
||||||
|
|
||||||
# note that this step is optional -> all ormar cares is a internal
|
# note that this step is optional -> all ormar cares is an individual
|
||||||
# class with name Meta and proper parameters, but this way you do not
|
# OrmarConfig for each of the models, but this way you do not
|
||||||
# have to repeat the same parameters if you use only one database
|
# have to repeat the same parameters if you use only one database
|
||||||
class BaseMeta(ormar.ModelMeta):
|
base_ormar_config = ormar.OrmarConfig(
|
||||||
metadata = metadata
|
database=databases.Database(DATABASE_URL),
|
||||||
database = database
|
metadata=sqlalchemy.MetaData(),
|
||||||
|
engine=sqlalchemy.create_engine(DATABASE_URL),
|
||||||
|
)
|
||||||
|
|
||||||
# Note that all type hints are optional
|
# Note that all type hints are optional
|
||||||
# below is a perfectly valid model declaration
|
# below is a perfectly valid model declaration
|
||||||
# class Author(ormar.Model):
|
# class Author(ormar.Model):
|
||||||
# class Meta(BaseMeta):
|
# ormar_config = base_ormar_config.copy(tablename="authors")
|
||||||
# tablename = "authors"
|
|
||||||
#
|
#
|
||||||
# id = ormar.Integer(primary_key=True) # <= notice no field types
|
# id = ormar.Integer(primary_key=True) # <= notice no field types
|
||||||
# name = ormar.String(max_length=100)
|
# name = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Author(ormar.Model):
|
class Author(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "authors"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
|
||||||
|
|
||||||
class Book(ormar.Model):
|
class Book(ormar.Model):
|
||||||
class Meta(BaseMeta):
|
ormar_config = base_ormar_config.copy()
|
||||||
tablename = "books"
|
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
@ -50,10 +45,9 @@ class Book(ormar.Model):
|
|||||||
# create the database
|
# create the database
|
||||||
# note that in production you should use migrations
|
# note that in production you should use migrations
|
||||||
# note that this is not required if you connect to existing database
|
# note that this is not required if you connect to existing database
|
||||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
|
||||||
# just to be sure we clear the db before
|
# just to be sure we clear the db before
|
||||||
metadata.drop_all(engine)
|
base_ormar_config.metadata.drop_all(base_ormar_config.engine)
|
||||||
metadata.create_all(engine)
|
base_ormar_config.metadata.create_all(base_ormar_config.engine)
|
||||||
|
|
||||||
|
|
||||||
# all functions below are divided into functionality categories
|
# all functions below are divided into functionality categories
|
||||||
@ -381,7 +375,7 @@ async def raw_data():
|
|||||||
async def with_connect(function):
|
async def with_connect(function):
|
||||||
# note that for any other backend than sqlite you actually need to
|
# note that for any other backend than sqlite you actually need to
|
||||||
# connect to the database to perform db operations
|
# connect to the database to perform db operations
|
||||||
async with database:
|
async with base_ormar_config.database:
|
||||||
await function()
|
await function()
|
||||||
|
|
||||||
# note that if you use framework like `fastapi` you shouldn't connect
|
# note that if you use framework like `fastapi` you shouldn't connect
|
||||||
@ -411,4 +405,4 @@ for func in [
|
|||||||
asyncio.run(with_connect(func))
|
asyncio.run(with_connect(func))
|
||||||
|
|
||||||
# drop the database tables
|
# drop the database tables
|
||||||
metadata.drop_all(engine)
|
base_ormar_config.metadata.drop_all(base_ormar_config.engine)
|
||||||
|
|||||||
21
examples/utils.py
Normal file
21
examples/utils.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import functools
|
||||||
|
|
||||||
|
import ormar
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
|
||||||
|
def create_drop_database(base_config: ormar.OrmarConfig) -> None:
|
||||||
|
# create all tables in the database before execution
|
||||||
|
# and drop them after, note that in production you should use migrations
|
||||||
|
def wrapper(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
async def wrapped(*args):
|
||||||
|
engine = sqlalchemy.create_engine(str(base_config.database.url))
|
||||||
|
base_config.metadata.drop_all(engine)
|
||||||
|
base_config.metadata.create_all(engine)
|
||||||
|
await func(*args)
|
||||||
|
base_config.metadata.drop_all(engine)
|
||||||
|
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
return wrapper
|
||||||
@ -16,10 +16,10 @@ nav:
|
|||||||
- Pydantic only fields: fields/pydantic-fields.md
|
- Pydantic only fields: fields/pydantic-fields.md
|
||||||
- Fields encryption: fields/encryption.md
|
- Fields encryption: fields/encryption.md
|
||||||
- Relations:
|
- Relations:
|
||||||
- relations/index.md
|
- Relation types: relations/index.md
|
||||||
- relations/postponed-annotations.md
|
|
||||||
- relations/foreign-key.md
|
- relations/foreign-key.md
|
||||||
- relations/many-to-many.md
|
- relations/many-to-many.md
|
||||||
|
- relations/postponed-annotations.md
|
||||||
- relations/queryset-proxy.md
|
- relations/queryset-proxy.md
|
||||||
- Queries:
|
- Queries:
|
||||||
- queries/index.md
|
- queries/index.md
|
||||||
@ -40,6 +40,7 @@ nav:
|
|||||||
- Using ormar in responses: fastapi/response.md
|
- Using ormar in responses: fastapi/response.md
|
||||||
- Using ormar in requests: fastapi/requests.md
|
- Using ormar in requests: fastapi/requests.md
|
||||||
- Use with mypy: mypy.md
|
- Use with mypy: mypy.md
|
||||||
|
- Migration to v 0.20: migration.md
|
||||||
- PyCharm plugin: plugin.md
|
- PyCharm plugin: plugin.md
|
||||||
- Contributing: contributing.md
|
- Contributing: contributing.md
|
||||||
- Release Notes: releases.md
|
- Release Notes: releases.md
|
||||||
|
|||||||
@ -19,11 +19,10 @@ snakes, and ormar(e) in italian which means cabinet.
|
|||||||
And what's a better name for python ORM than snakes cabinet :)
|
And what's a better name for python ORM than snakes cabinet :)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
from importlib.metadata import version # type: ignore
|
from ormar.protocols import QuerySetProtocol, RelationProtocol # noqa: I001
|
||||||
except ImportError: # pragma: no cover
|
from importlib.metadata import version
|
||||||
from importlib_metadata import version # type: ignore
|
|
||||||
from ormar.protocols import QuerySetProtocol, RelationProtocol # noqa: I100
|
|
||||||
from ormar.decorators import ( # noqa: I100
|
from ormar.decorators import ( # noqa: I100
|
||||||
post_bulk_update,
|
post_bulk_update,
|
||||||
post_delete,
|
post_delete,
|
||||||
@ -36,7 +35,6 @@ from ormar.decorators import ( # noqa: I100
|
|||||||
pre_relation_remove,
|
pre_relation_remove,
|
||||||
pre_save,
|
pre_save,
|
||||||
pre_update,
|
pre_update,
|
||||||
property_field,
|
|
||||||
)
|
)
|
||||||
from ormar.exceptions import ( # noqa: I100
|
from ormar.exceptions import ( # noqa: I100
|
||||||
ModelDefinitionError,
|
ModelDefinitionError,
|
||||||
@ -44,37 +42,38 @@ from ormar.exceptions import ( # noqa: I100
|
|||||||
NoMatch,
|
NoMatch,
|
||||||
)
|
)
|
||||||
from ormar.fields import (
|
from ormar.fields import (
|
||||||
|
DECODERS_MAP,
|
||||||
|
ENCODERS_MAP,
|
||||||
|
JSON,
|
||||||
|
SQL_ENCODERS_MAP,
|
||||||
|
UUID,
|
||||||
BaseField,
|
BaseField,
|
||||||
BigInteger,
|
BigInteger,
|
||||||
Boolean,
|
Boolean,
|
||||||
DECODERS_MAP,
|
CheckColumns,
|
||||||
Date,
|
Date,
|
||||||
DateTime,
|
DateTime,
|
||||||
Decimal,
|
Decimal,
|
||||||
ENCODERS_MAP,
|
|
||||||
EncryptBackends,
|
EncryptBackends,
|
||||||
Enum,
|
Enum,
|
||||||
Float,
|
Float,
|
||||||
ForeignKey,
|
ForeignKey,
|
||||||
ForeignKeyField,
|
ForeignKeyField,
|
||||||
|
IndexColumns,
|
||||||
Integer,
|
Integer,
|
||||||
JSON,
|
|
||||||
LargeBinary,
|
LargeBinary,
|
||||||
ManyToMany,
|
ManyToMany,
|
||||||
ManyToManyField,
|
ManyToManyField,
|
||||||
SQL_ENCODERS_MAP,
|
ReferentialAction,
|
||||||
SmallInteger,
|
SmallInteger,
|
||||||
String,
|
String,
|
||||||
Text,
|
Text,
|
||||||
Time,
|
Time,
|
||||||
UUID,
|
|
||||||
UniqueColumns,
|
UniqueColumns,
|
||||||
IndexColumns,
|
)
|
||||||
CheckColumns,
|
|
||||||
ReferentialAction,
|
# noqa: I100
|
||||||
) # noqa: I100
|
from ormar.models import ExcludableItems, Extra, Model, OrmarConfig
|
||||||
from ormar.models import ExcludableItems, Extra, Model
|
|
||||||
from ormar.models.metaclass import ModelMeta
|
|
||||||
from ormar.queryset import OrderAction, QuerySet, and_, or_
|
from ormar.queryset import OrderAction, QuerySet, and_, or_
|
||||||
from ormar.relations import RelationType
|
from ormar.relations import RelationType
|
||||||
from ormar.signals import Signal
|
from ormar.signals import Signal
|
||||||
@ -104,7 +103,6 @@ __all__ = [
|
|||||||
"Float",
|
"Float",
|
||||||
"ManyToMany",
|
"ManyToMany",
|
||||||
"Model",
|
"Model",
|
||||||
"Action",
|
|
||||||
"ModelDefinitionError",
|
"ModelDefinitionError",
|
||||||
"MultipleMatches",
|
"MultipleMatches",
|
||||||
"NoMatch",
|
"NoMatch",
|
||||||
@ -119,8 +117,6 @@ __all__ = [
|
|||||||
"ReferentialAction",
|
"ReferentialAction",
|
||||||
"QuerySetProtocol",
|
"QuerySetProtocol",
|
||||||
"RelationProtocol",
|
"RelationProtocol",
|
||||||
"ModelMeta",
|
|
||||||
"property_field",
|
|
||||||
"post_bulk_update",
|
"post_bulk_update",
|
||||||
"post_delete",
|
"post_delete",
|
||||||
"post_save",
|
"post_save",
|
||||||
@ -146,4 +142,5 @@ __all__ = [
|
|||||||
"DECODERS_MAP",
|
"DECODERS_MAP",
|
||||||
"LargeBinary",
|
"LargeBinary",
|
||||||
"Extra",
|
"Extra",
|
||||||
|
"OrmarConfig",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -3,11 +3,10 @@ Module with all decorators that are exposed for users.
|
|||||||
|
|
||||||
Currently only:
|
Currently only:
|
||||||
|
|
||||||
* property_field - exposing @property like function as field in Model.dict()
|
|
||||||
* predefined signals decorators (pre/post + save/update/delete)
|
* predefined signals decorators (pre/post + save/update/delete)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from ormar.decorators.property_field import property_field
|
|
||||||
from ormar.decorators.signals import (
|
from ormar.decorators.signals import (
|
||||||
post_bulk_update,
|
post_bulk_update,
|
||||||
post_delete,
|
post_delete,
|
||||||
@ -23,7 +22,6 @@ from ormar.decorators.signals import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"property_field",
|
|
||||||
"post_bulk_update",
|
"post_bulk_update",
|
||||||
"post_delete",
|
"post_delete",
|
||||||
"post_save",
|
"post_save",
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import inspect
|
|
||||||
from collections.abc import Callable
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from ormar.exceptions import ModelDefinitionError
|
|
||||||
|
|
||||||
|
|
||||||
def property_field(func: Callable) -> Union[property, Callable]:
|
|
||||||
"""
|
|
||||||
Decorator to set a property like function on Model to be exposed
|
|
||||||
as field in dict() and fastapi response.
|
|
||||||
Although you can decorate a @property field like this and this will work,
|
|
||||||
mypy validation will complain about this.
|
|
||||||
Note that "fields" exposed like this do not go through validation.
|
|
||||||
|
|
||||||
:raises ModelDefinitionError: if method has any other argument than self.
|
|
||||||
:param func: decorated function to be exposed
|
|
||||||
:type func: Callable
|
|
||||||
:return: decorated function passed in func param, with set __property_field__ = True
|
|
||||||
:rtype: Union[property, Callable]
|
|
||||||
"""
|
|
||||||
if isinstance(func, property): # pragma: no cover
|
|
||||||
func.fget.__property_field__ = True
|
|
||||||
else:
|
|
||||||
arguments = list(inspect.signature(func).parameters.keys())
|
|
||||||
if len(arguments) > 1 or arguments[0] != "self":
|
|
||||||
raise ModelDefinitionError(
|
|
||||||
"property_field decorator can be used "
|
|
||||||
"only on methods with no arguments"
|
|
||||||
)
|
|
||||||
func.__dict__["__property_field__"] = True
|
|
||||||
return func
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
from typing import Callable, List, TYPE_CHECKING, Type, Union
|
from typing import TYPE_CHECKING, Callable, List, Type, Union
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
from ormar import Model
|
from ormar import Model
|
||||||
@ -34,7 +34,7 @@ def receiver(
|
|||||||
else:
|
else:
|
||||||
_senders = senders
|
_senders = senders
|
||||||
for sender in _senders:
|
for sender in _senders:
|
||||||
signals = getattr(sender.Meta.signals, signal)
|
signals = getattr(sender.ormar_config.signals, signal)
|
||||||
signals.connect(func)
|
signals.connect(func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|||||||
@ -15,11 +15,9 @@ class ModelDefinitionError(AsyncOrmException):
|
|||||||
"""
|
"""
|
||||||
Raised for errors related to the model definition itself:
|
Raised for errors related to the model definition itself:
|
||||||
|
|
||||||
* setting @property_field on method with arguments other than func(self)
|
|
||||||
* defining a Field without required parameters
|
* defining a Field without required parameters
|
||||||
* defining a model with more than one primary_key
|
* defining a model with more than one primary_key
|
||||||
* defining a model without primary_key
|
* defining a model without primary_key
|
||||||
* setting primary_key column as pydantic_only
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user