update docs, cleaning
This commit is contained in:
@ -27,7 +27,7 @@ script:
|
|||||||
- DATABASE_URL=postgresql://localhost/test_database scripts/test.sh
|
- DATABASE_URL=postgresql://localhost/test_database scripts/test.sh
|
||||||
- DATABASE_URL=mysql://localhost/test_database scripts/test.sh
|
- DATABASE_URL=mysql://localhost/test_database scripts/test.sh
|
||||||
- DATABASE_URL=sqlite:///test.db 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:
|
after_script:
|
||||||
- codecov
|
- codecov
|
||||||
@ -44,9 +44,9 @@ git checkout -b my-new-feature-branch
|
|||||||
# 5. Formatting and linting
|
# 5. Formatting and linting
|
||||||
# ormar uses black for formatting, flake8 for linting and mypy for type hints check
|
# 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
|
# run all of the following as all those calls will be run on travis after every push
|
||||||
black ormar
|
black ormar tests
|
||||||
flake8 ormar
|
flake8 ormar
|
||||||
mypy --config-file mypy.ini ormar
|
mypy --config-file mypy.ini ormar tests
|
||||||
|
|
||||||
# 6. Run tests
|
# 6. Run tests
|
||||||
# on localhost all tests are run against sglite backend
|
# on localhost all tests are run against sglite backend
|
||||||
|
|||||||
@ -78,7 +78,7 @@ Used in sql only.
|
|||||||
|
|
||||||
Sample usage:
|
Sample usage:
|
||||||
|
|
||||||
```Python hl_lines="19-21"
|
```Python hl_lines="21-23"
|
||||||
--8<-- "../docs_src/fields/docs004.py"
|
--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
|
Note that you can also change the ForeignKey column name
|
||||||
```Python hl_lines="9"
|
```Python hl_lines="21"
|
||||||
--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="18"
|
```Python hl_lines="28"
|
||||||
--8<-- "../docs_src/models/docs010.py"
|
--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
|
### Database initialization/ migrations
|
||||||
|
|
||||||
Note that all examples assume that you already have a database.
|
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 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.
|
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
|
### 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'.
|
||||||
@ -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:
|
For example to list table model fields you can:
|
||||||
|
|
||||||
```Python hl_lines="19"
|
```Python hl_lines="20"
|
||||||
--8<-- "../docs_src/models/docs005.py"
|
--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
|
||||||
212
docs/queries.md
212
docs/queries.md
@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
## QuerySet
|
## 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.
|
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
|
Given the Models like this
|
||||||
|
|
||||||
```Python
|
```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.
|
Return number of rows updated.
|
||||||
|
|
||||||
```python hl_lines="24-28"
|
```Python hl_lines="26-28"
|
||||||
import databases
|
--8<-- "../docs_src/queries/docs002.py"
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!!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
|
||||||
|
|
||||||
`update_or_create(**kwargs) -> Model`
|
`update_or_create(**kwargs) -> Model`
|
||||||
|
|
||||||
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="24-30"
|
```Python hl_lines="26-32"
|
||||||
import databases
|
--8<-- "../docs_src/queries/docs003.py"
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!note
|
!!!note
|
||||||
@ -177,36 +130,8 @@ 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="20-26"
|
```python hl_lines="21-27"
|
||||||
import databases
|
--8<-- "../docs_src/queries/docs004.py"
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### bulk_update
|
### 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.
|
Return number of rows deleted.
|
||||||
|
|
||||||
```python hl_lines="23-27"
|
```python hl_lines="26-30"
|
||||||
import databases
|
--8<-- "../docs_src/queries/docs005.py"
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### all
|
### 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.
|
With `fields()` you can select subset of model columns to limit the data load.
|
||||||
|
|
||||||
```python hl_lines="48 60 61 67"
|
```python hl_lines="47 59 60 66"
|
||||||
import databases
|
--8<-- "../docs_src/queries/docs006.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')
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
!!!warning
|
!!!warning
|
||||||
@ -539,4 +370,5 @@ 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()`
|
Something like `Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()`
|
||||||
|
|
||||||
[models]: ./models.md
|
[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`.
|
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"
|
--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:
|
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"
|
--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:
|
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"
|
--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.
|
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"
|
--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.
|
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"
|
--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.
|
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"
|
--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).
|
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"
|
--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.
|
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
|
```python
|
||||||
# Creating columns object from instance:
|
# 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]
|
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
|
```python
|
||||||
# Removal of the relationship by one
|
|
||||||
await news.posts.remove(post)
|
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()
|
await news.posts.clear()
|
||||||
```
|
```
|
||||||
|
|
||||||
#### All other queryset methods
|
#### Other queryset methods
|
||||||
|
|
||||||
When access directly the related `ManyToMany` field returns the list of related models.
|
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
|
assert news_posts[0].author == guido
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Currently supported methods are:
|
||||||
|
|
||||||
!!!tip
|
!!!tip
|
||||||
To learn more about available QuerySet methods visit [queries][queries]
|
To learn more about available QuerySet methods visit [queries][queries]
|
||||||
|
|
||||||
|
##### get()
|
||||||
|
##### all()
|
||||||
|
##### filter()
|
||||||
|
##### select_related()
|
||||||
|
##### limit()
|
||||||
|
##### offset()
|
||||||
|
##### count()
|
||||||
|
##### exists()
|
||||||
|
|
||||||
[queries]: ./queries.md
|
[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
|
# 0.3.10
|
||||||
|
|
||||||
* Fix
|
* Fix postgresql check to avoid exceptions with drivers not installed if using different backend
|
||||||
|
|
||||||
# 0.3.9
|
# 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)
|
course = Course(name="Painting for dummies", completed=False)
|
||||||
await course.save() # type: ignore
|
await course.save()
|
||||||
await Course.objects.create(name="Painting for dummies", completed=False) # type: ignore
|
|
||||||
|
await Course.objects.create(name="Painting for dummies", completed=False)
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
from typing import Optional, Union, List
|
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
@ -27,6 +25,4 @@ class Artist(ormar.Model):
|
|||||||
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)
|
||||||
born_year: int = ormar.Integer(name="year")
|
born_year: int = ormar.Integer(name="year")
|
||||||
children: Optional[Union[Child, List[Child]]] = ormar.ManyToMany(
|
children = ormar.ManyToMany(Child, through=ArtistChildren)
|
||||||
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
|
- Relations: relations.md
|
||||||
- Queries: queries.md
|
- Queries: queries.md
|
||||||
- Use with Fastapi: fastapi.md
|
- Use with Fastapi: fastapi.md
|
||||||
|
- Use with mypy: mypy.md
|
||||||
|
- PyCharm plugin: plugin.md
|
||||||
- Contributing: contributing.md
|
- Contributing: contributing.md
|
||||||
- Release Notes: releases.md
|
- Release Notes: releases.md
|
||||||
repo_name: collerek/ormar
|
repo_name: collerek/ormar
|
||||||
|
|||||||
@ -40,7 +40,9 @@ class Artist(ormar.Model):
|
|||||||
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)
|
||||||
born_year: int = ormar.Integer(name="year")
|
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):
|
class Album(ormar.Model):
|
||||||
|
|||||||
@ -49,7 +49,9 @@ class Post(ormar.Model):
|
|||||||
|
|
||||||
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)
|
||||||
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)
|
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,9 @@ class Country(ormar.Model):
|
|||||||
database = database
|
database = database
|
||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
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)
|
taxed: bool = ormar.Boolean(choices=country_taxed_choices, default=True)
|
||||||
country_code: int = ormar.Integer(
|
country_code: int = ormar.Integer(
|
||||||
minimum=0, maximum=1000, choices=country_country_code_choices, default=1
|
minimum=0, maximum=1000, choices=country_country_code_choices, default=1
|
||||||
|
|||||||
Reference in New Issue
Block a user