update docs, cleaning
This commit is contained in:
@ -27,7 +27,7 @@ script:
|
||||
- DATABASE_URL=postgresql://localhost/test_database scripts/test.sh
|
||||
- DATABASE_URL=mysql://localhost/test_database scripts/test.sh
|
||||
- DATABASE_URL=sqlite:///test.db scripts/test.sh
|
||||
- mypy --config-file mypy.ini ormar
|
||||
- mypy --config-file mypy.ini ormar tests
|
||||
|
||||
after_script:
|
||||
- codecov
|
||||
@ -44,9 +44,9 @@ git checkout -b my-new-feature-branch
|
||||
# 5. Formatting and linting
|
||||
# ormar uses black for formatting, flake8 for linting and mypy for type hints check
|
||||
# run all of the following as all those calls will be run on travis after every push
|
||||
black ormar
|
||||
black ormar tests
|
||||
flake8 ormar
|
||||
mypy --config-file mypy.ini ormar
|
||||
mypy --config-file mypy.ini ormar tests
|
||||
|
||||
# 6. Run tests
|
||||
# on localhost all tests are run against sglite backend
|
||||
|
||||
@ -78,7 +78,7 @@ Used in sql only.
|
||||
|
||||
Sample usage:
|
||||
|
||||
```Python hl_lines="19-21"
|
||||
```Python hl_lines="21-23"
|
||||
--8<-- "../docs_src/fields/docs004.py"
|
||||
```
|
||||
|
||||
|
||||
@ -50,14 +50,44 @@ Here you have a sample model with changed names
|
||||
```
|
||||
|
||||
Note that you can also change the ForeignKey column name
|
||||
```Python hl_lines="9"
|
||||
```Python hl_lines="21"
|
||||
--8<-- "../docs_src/models/docs009.py"
|
||||
```
|
||||
|
||||
But for now you cannot change the ManyToMany column names as they go through other Model anyway.
|
||||
```Python hl_lines="18"
|
||||
```Python hl_lines="28"
|
||||
--8<-- "../docs_src/models/docs010.py"
|
||||
```
|
||||
|
||||
### Type Hints & Legacy
|
||||
|
||||
Before version 0.4.0 `ormar` supported only one way of defining `Fields` on a `Model` using python type hints as pydantic.
|
||||
|
||||
```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"
|
||||
--8<-- "../docs_src/models/docs001.py"
|
||||
```
|
||||
|
||||
Note that type hints are **optional** so perfectly valid `ormar` code can look like this:
|
||||
|
||||
```Python hl_lines="15-17"
|
||||
--8<-- "../docs_src/models/docs001.py"
|
||||
```
|
||||
|
||||
!!!warning
|
||||
Even if you use type hints **`ormar` does not use them to construct `pydantic` fields!**
|
||||
|
||||
Type hints are there only to support static checkers and linting,
|
||||
`ormar` construct annotations used by `pydantic` from own fields.
|
||||
|
||||
|
||||
### Database initialization/ migrations
|
||||
|
||||
Note that all examples assume that you already have a database.
|
||||
@ -133,6 +163,20 @@ Created instance needs to be passed to every `Model` with `Meta` class `metadata
|
||||
You need to create the `MetaData` instance **only once** and use it for all models.
|
||||
You can create several ones if you want to use multiple databases.
|
||||
|
||||
#### Best practice
|
||||
|
||||
Only thing that `ormar` expects is a class with name `Meta` and two class variables: `metadata` and `databases`.
|
||||
|
||||
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="14 20 33"
|
||||
--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
|
||||
|
||||
By default table name is created from Model class name as lowercase name plus 's'.
|
||||
@ -278,7 +322,7 @@ To access ormar `Fields` you can use `Model.Meta.model_fields` parameter
|
||||
|
||||
For example to list table model fields you can:
|
||||
|
||||
```Python hl_lines="19"
|
||||
```Python hl_lines="20"
|
||||
--8<-- "../docs_src/models/docs005.py"
|
||||
```
|
||||
|
||||
|
||||
28
docs/mypy.md
Normal file
28
docs/mypy.md
Normal file
@ -0,0 +1,28 @@
|
||||
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.
|
||||
|
||||
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"
|
||||
--8<-- "../docs_src/models/docs012.py"
|
||||
```
|
||||
|
||||
Note that above example is not using the type hints, so further operations with mypy might fail, depending on the context.
|
||||
|
||||
Preferred notation should look liked this:
|
||||
|
||||
```Python hl_lines="15-17"
|
||||
--8<-- "../docs_src/models/docs001.py"
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[plugin]: https://pydantic-docs.helpmanual.io/mypy_plugin/
|
||||
18
docs/plugin.md
Normal file
18
docs/plugin.md
Normal file
@ -0,0 +1,18 @@
|
||||
While `ormar` will work with any IDE there is a PyCharm `pydantic` plugin that enhances the user experience for this IDE.
|
||||
|
||||
Plugin is available on the JetBrains Plugins Repository for PyCharm: [plugin page][plugin page].
|
||||
|
||||
You can install the plugin for free from the plugin marketplace
|
||||
(PyCharm's Preferences -> Plugin -> Marketplace -> search "pydantic").
|
||||
|
||||
!!!note
|
||||
For plugin to work properly you need to provide valid type hints for model fields.
|
||||
|
||||
!!!info
|
||||
Plugin supports type hints, argument inspection and more but mainly only for __init__ methods
|
||||
|
||||
More information can be found on the
|
||||
[official plugin page](https://plugins.jetbrains.com/plugin/12861-pydantic)
|
||||
and [github repository](https://github.com/koxudaxi/pydantic-pycharm-plugin).
|
||||
|
||||
[plugin page]: https://plugins.jetbrains.com/plugin/12861-pydantic
|
||||
210
docs/queries.md
210
docs/queries.md
@ -2,10 +2,13 @@
|
||||
|
||||
## QuerySet
|
||||
|
||||
Each Model is auto registered with a QuerySet that represents the underlaying query and it's options.
|
||||
Each Model is auto registered with a `QuerySet` that represents the underlaying query and it's options.
|
||||
|
||||
Most of the methods are also available through many to many relation interface.
|
||||
|
||||
!!!info
|
||||
To see which one are supported and how to construct relations visit [relations][relations].
|
||||
|
||||
Given the Models like this
|
||||
|
||||
```Python
|
||||
@ -95,74 +98,24 @@ If you do not provide this flag or a filter a `QueryDefinitionError` will be rai
|
||||
|
||||
Return number of rows updated.
|
||||
|
||||
```python hl_lines="24-28"
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "books"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
author: str = ormar.String(max_length=100)
|
||||
genre: str = ormar.String(max_length=100, default='Fiction', choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
||||
|
||||
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')
|
||||
|
||||
|
||||
# queryset needs to be filtered before deleting to prevent accidental overwrite
|
||||
# to update whole database table each=True needs to be provided as a safety switch
|
||||
await Book.objects.update(each=True, genre='Fiction')
|
||||
all_books = await Book.objects.filter(genre='Fiction').all()
|
||||
assert len(all_books) == 3
|
||||
```Python hl_lines="26-28"
|
||||
--8<-- "../docs_src/queries/docs002.py"
|
||||
```
|
||||
|
||||
!!!warning
|
||||
Queryset needs to be filtered before updating to prevent accidental overwrite.
|
||||
|
||||
To update whole database table `each=True` needs to be provided as a safety switch
|
||||
|
||||
|
||||
### update_or_create
|
||||
|
||||
`update_or_create(**kwargs) -> Model`
|
||||
|
||||
Updates the model, or in case there is no match in database creates a new one.
|
||||
|
||||
```python hl_lines="24-30"
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "books"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
author: str = ormar.String(max_length=100)
|
||||
genre: str = ormar.String(max_length=100, default='Fiction', choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
||||
|
||||
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
|
||||
vol2 = await Book.objects.update_or_create(title="Volume II", author='Anonymous', genre='Fiction')
|
||||
assert await Book.objects.count() == 1
|
||||
|
||||
# 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.count() == 1
|
||||
```Python hl_lines="26-32"
|
||||
--8<-- "../docs_src/queries/docs003.py"
|
||||
```
|
||||
|
||||
!!!note
|
||||
@ -177,36 +130,8 @@ Allows you to create multiple objects at once.
|
||||
|
||||
A valid list of `Model` objects needs to be passed.
|
||||
|
||||
```python hl_lines="20-26"
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class ToDo(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "todos"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
text: str = ormar.String(max_length=500)
|
||||
completed= ormar.Boolean(default=False)
|
||||
|
||||
# create multiple instances at once with bulk_create
|
||||
await ToDo.objects.bulk_create(
|
||||
[
|
||||
ToDo(text="Buy the groceries."),
|
||||
ToDo(text="Call Mum.", completed=True),
|
||||
ToDo(text="Send invoices.", completed=True),
|
||||
]
|
||||
)
|
||||
|
||||
todoes = await ToDo.objects.all()
|
||||
assert len(todoes) == 3
|
||||
```python hl_lines="21-27"
|
||||
--8<-- "../docs_src/queries/docs004.py"
|
||||
```
|
||||
|
||||
### bulk_update
|
||||
@ -245,34 +170,8 @@ If you do not provide this flag or a filter a `QueryDefinitionError` will be rai
|
||||
|
||||
Return number of rows deleted.
|
||||
|
||||
```python hl_lines="23-27"
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "books"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
author: str = ormar.String(max_length=100)
|
||||
genre: str = ormar.String(max_length=100, default='Fiction', choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
||||
|
||||
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
|
||||
# acting in same way as queryset.filter(**kwargs).delete()
|
||||
await Book.objects.delete(genre='Fantasy') # delete all fantasy books
|
||||
all_books = await Book.objects.all()
|
||||
assert len(all_books) == 2
|
||||
```python hl_lines="26-30"
|
||||
--8<-- "../docs_src/queries/docs005.py"
|
||||
```
|
||||
|
||||
### all
|
||||
@ -453,76 +352,8 @@ has_sample = await Book.objects.filter(title='Sample').exists()
|
||||
|
||||
With `fields()` you can select subset of model columns to limit the data load.
|
||||
|
||||
```python hl_lines="48 60 61 67"
|
||||
import databases
|
||||
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')
|
||||
|
||||
# select manufacturer but only name - to include related models use notation {model_name}__{column}
|
||||
all_cars = await Car.objects.select_related('manufacturer').fields(['id', 'name', 'company__name']).all()
|
||||
for car in all_cars:
|
||||
# excluded columns will yield None
|
||||
assert all(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
|
||||
assert car.manufacturer.name == 'Toyota'
|
||||
# also in the nested related models - you cannot exclude pk - it's always auto added
|
||||
assert car.manufacturer.founded is None
|
||||
|
||||
# fields() can be called several times, building up the columns to select
|
||||
# models selected in select_related but with no columns in fields list implies all fields
|
||||
all_cars = await Car.objects.select_related('manufacturer').fields('id').fields(
|
||||
['name']).all()
|
||||
# all fiels from company model are selected
|
||||
assert all_cars[0].manufacturer.name == 'Toyota'
|
||||
assert all_cars[0].manufacturer.founded == 1937
|
||||
|
||||
# cannot exclude mandatory model columns - company__name in this example
|
||||
await Car.objects.select_related('manufacturer').fields(['id', 'name', 'company__founded']).all()
|
||||
# will raise pydantic ValidationError as company.name is required
|
||||
|
||||
```python hl_lines="47 59 60 66"
|
||||
--8<-- "../docs_src/queries/docs006.py"
|
||||
```
|
||||
|
||||
!!!warning
|
||||
@ -540,3 +371,4 @@ await Car.objects.select_related('manufacturer').fields(['id', 'name', 'company_
|
||||
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
|
||||
|
||||
[models]: ./models.md
|
||||
[relations]: ./relations.md
|
||||
@ -15,7 +15,7 @@ Sqlalchemy column and Type are automatically taken from target `Model`.
|
||||
|
||||
To define a relation add `ForeignKey` field that points to related `Model`.
|
||||
|
||||
```Python hl_lines="27"
|
||||
```Python hl_lines="29"
|
||||
--8<-- "../docs_src/fields/docs003.py"
|
||||
```
|
||||
|
||||
@ -25,7 +25,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:
|
||||
|
||||
```Python hl_lines="27 33"
|
||||
```Python hl_lines="29 35"
|
||||
--8<-- "../docs_src/fields/docs001.py"
|
||||
```
|
||||
|
||||
@ -33,7 +33,7 @@ By default it's child (source) `Model` name + s, like courses in snippet below:
|
||||
|
||||
But you can overwrite this name by providing `related_name` parameter like below:
|
||||
|
||||
```Python hl_lines="27 33"
|
||||
```Python hl_lines="29 35"
|
||||
--8<-- "../docs_src/fields/docs002.py"
|
||||
```
|
||||
|
||||
@ -49,7 +49,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.
|
||||
|
||||
```Python hl_lines="32-33"
|
||||
```Python hl_lines="34-35"
|
||||
--8<-- "../docs_src/relations/docs001.py"
|
||||
```
|
||||
|
||||
@ -57,7 +57,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.
|
||||
|
||||
```Python hl_lines="35-36"
|
||||
```Python hl_lines="37-38"
|
||||
--8<-- "../docs_src/relations/docs001.py"
|
||||
```
|
||||
|
||||
@ -67,7 +67,7 @@ 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.
|
||||
|
||||
```Python hl_lines="38-39"
|
||||
```Python hl_lines="40-41"
|
||||
--8<-- "../docs_src/relations/docs001.py"
|
||||
```
|
||||
|
||||
@ -75,7 +75,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).
|
||||
|
||||
```Python hl_lines="41-42"
|
||||
```Python hl_lines="43-44"
|
||||
--8<-- "../docs_src/relations/docs001.py"
|
||||
```
|
||||
|
||||
@ -121,7 +121,11 @@ await news.posts.add(post)
|
||||
|
||||
Otherwise an IntegrityError will be raised by your database driver library.
|
||||
|
||||
#### Creating new related `Model` instances
|
||||
#### create()
|
||||
|
||||
Create related `Model` directly from parent `Model`.
|
||||
|
||||
The link table is automatically populated, as well as relation ids in the database.
|
||||
|
||||
```python
|
||||
# Creating columns object from instance:
|
||||
@ -136,15 +140,27 @@ assert len(await post.categories.all()) == 2
|
||||
|
||||
To learn more about available QuerySet methods visit [queries][queries]
|
||||
|
||||
#### Removing related models
|
||||
#### remove()
|
||||
|
||||
Removal of the related model one by one.
|
||||
|
||||
Removes also the relation in the database.
|
||||
|
||||
```python
|
||||
# Removal of the relationship by one
|
||||
await news.posts.remove(post)
|
||||
# or all at once
|
||||
```
|
||||
|
||||
#### clear()
|
||||
|
||||
Removal all related models in one call.
|
||||
|
||||
Removes also the relation in the database.
|
||||
|
||||
```python
|
||||
await news.posts.clear()
|
||||
```
|
||||
|
||||
#### All other queryset methods
|
||||
#### Other queryset methods
|
||||
|
||||
When access directly the related `ManyToMany` field returns the list of related models.
|
||||
|
||||
@ -164,7 +180,18 @@ news_posts = await news.posts.select_related("author").all()
|
||||
assert news_posts[0].author == guido
|
||||
```
|
||||
|
||||
Currently supported methods are:
|
||||
|
||||
!!!tip
|
||||
To learn more about available QuerySet methods visit [queries][queries]
|
||||
|
||||
##### get()
|
||||
##### all()
|
||||
##### filter()
|
||||
##### select_related()
|
||||
##### limit()
|
||||
##### offset()
|
||||
##### count()
|
||||
##### exists()
|
||||
|
||||
[queries]: ./queries.md
|
||||
@ -1,6 +1,22 @@
|
||||
# 0.4.0
|
||||
|
||||
* Changed notation in Model definition -> now use name = ormar.Field() not name: ormar.Field()
|
||||
* Note that old notation is still supported but deprecated and will not play nice with static checkers like mypy and pydantic pycharm plugin
|
||||
* Type hint docs and test
|
||||
* Use mypy for tests also not, only ormar package
|
||||
* Fix scale and precision translation with max_digits and decimal_places pydantic Decimal field
|
||||
* Update docs - add best practices for dependencies
|
||||
* Refactor metaclass and model_fields to play nice with type hints
|
||||
* Add mypy and pydantic plugin to docs
|
||||
* Expand the docs on ManyToMany relation
|
||||
|
||||
# 0.3.11
|
||||
|
||||
* Fix setting server_default as default field value in python
|
||||
|
||||
# 0.3.10
|
||||
|
||||
* Fix
|
||||
* Fix postgresql check to avoid exceptions with drivers not installed if using different backend
|
||||
|
||||
# 0.3.9
|
||||
|
||||
|
||||
0
docs_src/fastapi/mypy/__init__.py
Normal file
0
docs_src/fastapi/mypy/__init__.py
Normal file
17
docs_src/fastapi/mypy/docs001.py
Normal file
17
docs_src/fastapi/mypy/docs001.py
Normal file
@ -0,0 +1,17 @@
|
||||
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)
|
||||
@ -18,5 +18,6 @@ class Course(ormar.Model):
|
||||
|
||||
|
||||
course = Course(name="Painting for dummies", completed=False)
|
||||
await course.save() # type: ignore
|
||||
await Course.objects.create(name="Painting for dummies", completed=False) # type: ignore
|
||||
await course.save()
|
||||
|
||||
await Course.objects.create(name="Painting for dummies", completed=False)
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
from typing import Optional, Union, List
|
||||
|
||||
import databases
|
||||
import sqlalchemy
|
||||
|
||||
@ -27,6 +25,4 @@ class Artist(ormar.Model):
|
||||
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")
|
||||
children: Optional[Union[Child, List[Child]]] = ormar.ManyToMany(
|
||||
Child, through=ArtistChildren
|
||||
)
|
||||
children = ormar.ManyToMany(Child, through=ArtistChildren)
|
||||
|
||||
19
docs_src/models/docs011.py
Normal file
19
docs_src/models/docs011.py
Normal file
@ -0,0 +1,19 @@
|
||||
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()
|
||||
17
docs_src/models/docs012.py
Normal file
17
docs_src/models/docs012.py
Normal file
@ -0,0 +1,17 @@
|
||||
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)
|
||||
38
docs_src/models/docs013.py
Normal file
38
docs_src/models/docs013.py
Normal file
@ -0,0 +1,38 @@
|
||||
from typing import Optional
|
||||
|
||||
import databases
|
||||
import sqlalchemy
|
||||
|
||||
import ormar
|
||||
|
||||
database = databases.Database("sqlite:///test.db", force_rollback=True)
|
||||
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 Meta(MainMeta):
|
||||
# note that tablename is optional
|
||||
# if not provided ormar will user class.__name__.lower()+'s'
|
||||
# -> artists in this example
|
||||
pass
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
first_name: str = ormar.String(max_length=100)
|
||||
last_name: str = ormar.String(max_length=100)
|
||||
born_year: int = ormar.Integer(name="year")
|
||||
|
||||
|
||||
class Album(ormar.Model):
|
||||
class Meta(MainMeta):
|
||||
pass
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
artist: Optional[Artist] = ormar.ForeignKey(Artist)
|
||||
28
docs_src/queries/docs002.py
Normal file
28
docs_src/queries/docs002.py
Normal file
@ -0,0 +1,28 @@
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "books"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
author: str = ormar.String(max_length=100)
|
||||
genre: str = ormar.String(max_length=100, default='Fiction',
|
||||
choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
||||
|
||||
|
||||
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')
|
||||
all_books = await Book.objects.filter(genre='Fiction').all()
|
||||
assert len(all_books) == 3
|
||||
32
docs_src/queries/docs003.py
Normal file
32
docs_src/queries/docs003.py
Normal file
@ -0,0 +1,32 @@
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "books"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
author: str = ormar.String(max_length=100)
|
||||
genre: str = ormar.String(max_length=100, default='Fiction',
|
||||
choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
||||
|
||||
|
||||
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
|
||||
vol2 = await Book.objects.update_or_create(title="Volume II", author='Anonymous', genre='Fiction')
|
||||
assert await Book.objects.count() == 1
|
||||
|
||||
# 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.count() == 1
|
||||
30
docs_src/queries/docs004.py
Normal file
30
docs_src/queries/docs004.py
Normal file
@ -0,0 +1,30 @@
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class ToDo(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "todos"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
text: str = ormar.String(max_length=500)
|
||||
completed = ormar.Boolean(default=False)
|
||||
|
||||
|
||||
# create multiple instances at once with bulk_create
|
||||
await ToDo.objects.bulk_create(
|
||||
[
|
||||
ToDo(text="Buy the groceries."),
|
||||
ToDo(text="Call Mum.", completed=True),
|
||||
ToDo(text="Send invoices.", completed=True),
|
||||
]
|
||||
)
|
||||
|
||||
todoes = await ToDo.objects.all()
|
||||
assert len(todoes) == 3
|
||||
30
docs_src/queries/docs005.py
Normal file
30
docs_src/queries/docs005.py
Normal file
@ -0,0 +1,30 @@
|
||||
import databases
|
||||
import ormar
|
||||
import sqlalchemy
|
||||
|
||||
database = databases.Database("sqlite:///db.sqlite")
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "books"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
author: str = ormar.String(max_length=100)
|
||||
genre: str = ormar.String(max_length=100, default='Fiction',
|
||||
choices=['Fiction', 'Adventure', 'Historic', 'Fantasy'])
|
||||
|
||||
|
||||
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
|
||||
# acting in same way as queryset.filter(**kwargs).delete()
|
||||
await Book.objects.delete(genre='Fantasy') # delete all fantasy books
|
||||
all_books = await Book.objects.all()
|
||||
assert len(all_books) == 2
|
||||
67
docs_src/queries/docs006.py
Normal file
67
docs_src/queries/docs006.py
Normal file
@ -0,0 +1,67 @@
|
||||
import databases
|
||||
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')
|
||||
|
||||
# select manufacturer but only name - to include related models use notation {model_name}__{column}
|
||||
all_cars = await Car.objects.select_related('manufacturer').fields(['id', 'name', 'company__name']).all()
|
||||
for car in all_cars:
|
||||
# excluded columns will yield None
|
||||
assert all(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
|
||||
assert car.manufacturer.name == 'Toyota'
|
||||
# also in the nested related models - you cannot exclude pk - it's always auto added
|
||||
assert car.manufacturer.founded is None
|
||||
|
||||
# fields() can be called several times, building up the columns to select
|
||||
# models selected in select_related but with no columns in fields list implies all fields
|
||||
all_cars = await Car.objects.select_related('manufacturer').fields('id').fields(
|
||||
['name']).all()
|
||||
# all fiels from company model are selected
|
||||
assert all_cars[0].manufacturer.name == 'Toyota'
|
||||
assert all_cars[0].manufacturer.founded == 1937
|
||||
|
||||
# cannot exclude mandatory model columns - company__name in this example
|
||||
await Car.objects.select_related('manufacturer').fields(['id', 'name', 'company__founded']).all()
|
||||
# will raise pydantic ValidationError as company.name is required
|
||||
@ -8,6 +8,8 @@ nav:
|
||||
- Relations: relations.md
|
||||
- Queries: queries.md
|
||||
- Use with Fastapi: fastapi.md
|
||||
- Use with mypy: mypy.md
|
||||
- PyCharm plugin: plugin.md
|
||||
- Contributing: contributing.md
|
||||
- Release Notes: releases.md
|
||||
repo_name: collerek/ormar
|
||||
|
||||
@ -40,7 +40,9 @@ class Artist(ormar.Model):
|
||||
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")
|
||||
children: Optional[Union[Child, List[Child]]] = ormar.ManyToMany(Child, through=ArtistChildren)
|
||||
children: Optional[Union[Child, List[Child]]] = ormar.ManyToMany(
|
||||
Child, through=ArtistChildren
|
||||
)
|
||||
|
||||
|
||||
class Album(ormar.Model):
|
||||
|
||||
@ -49,7 +49,9 @@ class Post(ormar.Model):
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
categories: Optional[Union[Category, List[Category]]] = ormar.ManyToMany(Category, through=PostCategory)
|
||||
categories: Optional[Union[Category, List[Category]]] = ormar.ManyToMany(
|
||||
Category, through=PostCategory
|
||||
)
|
||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||
|
||||
|
||||
|
||||
@ -71,7 +71,9 @@ class Country(ormar.Model):
|
||||
database = database
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=9, choices=country_name_choices, default="Canada",)
|
||||
name: str = ormar.String(
|
||||
max_length=9, choices=country_name_choices, default="Canada",
|
||||
)
|
||||
taxed: bool = ormar.Boolean(choices=country_taxed_choices, default=True)
|
||||
country_code: int = ormar.Integer(
|
||||
minimum=0, maximum=1000, choices=country_country_code_choices, default=1
|
||||
|
||||
Reference in New Issue
Block a user