diff --git a/README.md b/README.md index eb3705702..34c63b5e8 100644 --- a/README.md +++ b/README.md @@ -171,39 +171,31 @@ import ormar import sqlalchemy 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 -# 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 +base_ormar_config = ormar.OrmarConfig( + database=databases.Database(DATABASE_URL), + metadata=sqlalchemy.MetaData(), + engine=sqlalchemy.create_engine(DATABASE_URL), +) # Note that all type hints are optional # below is a perfectly valid model declaration # class Author(ormar.Model): -# class Meta(BaseMeta): -# tablename = "authors" +# ormar_config = base_ormar_config.copy(tablename="authors") # # id = ormar.Integer(primary_key=True) # <= notice no field types # name = ormar.String(max_length=100) class Author(ormar.Model): - class Meta(BaseMeta): - tablename = "authors" + ormar_config = base_ormar_config.copy(tablename="authors") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Book(ormar.Model): - class Meta(BaseMeta): - tablename = "books" + ormar_config = base_ormar_config.copy(tablename="books") id: int = ormar.Integer(primary_key=True) author: Optional[Author] = ormar.ForeignKey(Author) @@ -214,10 +206,9 @@ class Book(ormar.Model): # create the database # note that in production you should use migrations # 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 -metadata.drop_all(engine) -metadata.create_all(engine) +base_ormar_config.metadata.drop_all(base_ormar_config.engine) +base_ormar_config.metadata.create_all(base_ormar_config.engine) # all functions below are divided into functionality categories @@ -546,7 +537,7 @@ async def raw_data(): async def with_connect(function): # note that for any other backend than sqlite you actually need to # connect to the database to perform db operations - async with database: + async with base_ormar_config.database: await function() # note that if you use framework like `fastapi` you shouldn't connect @@ -576,7 +567,7 @@ for func in [ asyncio.run(with_connect(func)) # drop the database tables -metadata.drop_all(engine) +base_ormar_config.metadata.drop_all(base_ormar_config.engine) ``` ## Ormar Specification diff --git a/docs/fastapi/index.md b/docs/fastapi/index.md index 25521a73c..4807a0c18 100644 --- a/docs/fastapi/index.md +++ b/docs/fastapi/index.md @@ -45,13 +45,10 @@ from fastapi import FastAPI async def lifespan(_: FastAPI) -> AsyncIterator[None]: if not config.database.is_connected: await config.database.connect() - config.metadata.drop_all(config.engine) - config.metadata.create_all(config.engine) yield if config.database.is_connected: - config.metadata.drop_all(config.engine) await config.database.disconnect() @@ -72,21 +69,21 @@ Define ormar models with appropriate fields. Those models will be used instead of pydantic ones. ```python +base_ormar_config = OrmarConfig( + metadata = metadata + database = database +) + + class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Item(ormar.Model): - class Meta: - tablename = "items" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) diff --git a/docs/fastapi/requests.md b/docs/fastapi/requests.md index b2a5e829e..181b715f5 100644 --- a/docs/fastapi/requests.md +++ b/docs/fastapi/requests.md @@ -23,11 +23,13 @@ Field is not required if (any/many/all) of following: Example: ```python +base_ormar_config = ormar.OrmarConfig( + metadata=metadata + database=database +) + class User(ormar.Model): - class Meta: - tablename: str = "users" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) email: str = ormar.String(max_length=255) diff --git a/docs/fastapi/response.md b/docs/fastapi/response.md index 8b3f8e009..3949bfeb9 100644 --- a/docs/fastapi/response.md +++ b/docs/fastapi/response.md @@ -22,11 +22,13 @@ Field is not required if (any/many/all) of following: Example: ```python +base_ormar_config = ormar.OrmarConfig( + metadata=metadata + database=database +) + class User(ormar.Model): - class Meta: - tablename: str = "users" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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: ```python -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database +base_ormar_config = ormar.OrmarConfig( + metadata=metadata + database=database +) class Category(ormar.Model): - class Meta(BaseMeta): - tablename: str = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=255) @@ -125,8 +127,7 @@ class Category(ormar.Model): class User(ormar.Model): - class Meta(BaseMeta): - tablename: str = "users" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) email: str = ormar.String(max_length=255) diff --git a/docs/fields/common-parameters.md b/docs/fields/common-parameters.md index bee0f4150..80800c191 100644 --- a/docs/fields/common-parameters.md +++ b/docs/fields/common-parameters.md @@ -108,7 +108,7 @@ Used in sql only. Sample usage: -```Python hl_lines="21-23" +```Python hl_lines="20-22" --8<-- "../docs_src/fields/docs004.py" ``` @@ -184,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! ```python +base_ormar_config = ormar.OrmarConfig( + metadata=metadata + database=database +) + + # sample overwrites class OverwriteTest(ormar.Model): - class Meta: - tablename = "overwrites" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="overwrites") id: int = ormar.Integer(primary_key=True) my_int: str = ormar.Integer(overwrite_pydantic_type=PositiveInt) diff --git a/docs/fields/encryption.md b/docs/fields/encryption.md index 9cc06b05d..7fe5be968 100644 --- a/docs/fields/encryption.md +++ b/docs/fields/encryption.md @@ -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. -```python hl_lines="7-8" +```python hl_lines="10-12" +base_ormar_config = ormar.OrmarConfig( + metadata=metadata + database=database +) + class Filter(ormar.Model): - class Meta(BaseMeta): - tablename = "filters" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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 class Hash(ormar.Model): - class Meta(BaseMeta): - tablename = "hashes" + ormar_config = base_ormar_config.copy(tablename="hashes") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=128, @@ -106,8 +109,7 @@ as the returned value is parsed to corresponding python type. ```python class Filter(ormar.Model): - class Meta(BaseMeta): - tablename = "filters" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, @@ -152,8 +154,7 @@ argument by `encrypt_custom_backend`. ```python class Filter(ormar.Model): - class Meta(BaseMeta): - tablename = "filters" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, @@ -161,4 +162,4 @@ class Filter(ormar.Model): encrypt_backend=ormar.EncryptBackends.CUSTOM, encrypt_custom_backend=DummyBackend ) -``` \ No newline at end of file +``` diff --git a/docs/fields/field-types.md b/docs/fields/field-types.md index 4bb25e4a9..4cafaf2c7 100644 --- a/docs/fields/field-types.md +++ b/docs/fields/field-types.md @@ -160,11 +160,16 @@ That way you can i.e. set the value by API, even if value is not `utf-8` compati ```python import base64 ... # other imports skipped for brevity + + +base_ormar_config = ormar.OrmarConfig( + metadata=metadata + database=database +) + + class LargeBinaryStr(ormar.Model): - class Meta: - tablename = "my_str_blobs" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="my_str_blobs") id: int = ormar.Integer(primary_key=True) test_binary: str = ormar.LargeBinary( @@ -233,10 +238,7 @@ class TestEnum(Enum): val2 = 'Val2' class TestModel(ormar.Model): - class Meta: - tablename = "org" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="org") id: int = ormar.Integer(primary_key=True) # pass list(Enum) to choices diff --git a/docs/fields/pydantic-fields.md b/docs/fields/pydantic-fields.md index 042bd3654..800e349ee 100644 --- a/docs/fields/pydantic-fields.md +++ b/docs/fields/pydantic-fields.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. ```python -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 ModelTest(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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`. ```python -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 ModelTest(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=200) @@ -97,13 +91,12 @@ on initialization and each database load. 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 = [ "123456789007", @@ -119,8 +112,7 @@ def get_number(): class ModelTest2(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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 # ... -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): aa: str @@ -163,8 +154,7 @@ class PydanticTest(BaseModel): class ModelTest3(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() # provide your custom init function def __init__(self, **kwargs): @@ -192,4 +182,4 @@ assert test_check.pydantic_test.aa == "random" ``` !!!warning - If you do not provide a value in one of the above ways `ValidationError` will be raised on load from database. \ No newline at end of file + If you do not provide a value in one of the above ways `ValidationError` will be raised on load from database. diff --git a/docs/index.md b/docs/index.md index bb75b91aa..88b27340d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -128,17 +128,20 @@ For tests and basic applications the `sqlalchemy` is more than enough: # 1. Imports import sqlalchemy import databases +import ormar # 2. Initialization DATABASE_URL = "sqlite:///db.sqlite" -database = databases.Database(DATABASE_URL) -metadata = sqlalchemy.MetaData() +base_ormar_config = ormar.OrmarConfig( + metadata=sqlalchemy.MetaData(), + database=databases.Database(DATABASE_URL), + engine=sqlalchemy.create_engine(DATABASE_URL), +) # Define models here # 3. Database creation and tables creation -engine = sqlalchemy.create_engine(DATABASE_URL) -metadata.create_all(engine) +base_ormar_config.metadata.create_all(engine) ``` For a sample configuration of alembic and more information regarding migrations and @@ -175,45 +178,39 @@ Note that you can find the same script in examples folder on github. from typing import Optional import databases -import pydantic import ormar import sqlalchemy 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 -# 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 - - +base_ormar_config = ormar.OrmarConfig( + 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 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 # below is a perfectly valid model declaration # class Author(ormar.Model): -# class Meta(BaseMeta): -# tablename = "authors" +# ormar_config = base_ormar_config.copy(tablename="authors") # # id = ormar.Integer(primary_key=True) # <= notice no field types # name = ormar.String(max_length=100) class Author(ormar.Model): - class Meta(BaseMeta): - tablename = "authors" + ormar_config = base_ormar_config.copy(tablename="authors") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Book(ormar.Model): - class Meta(BaseMeta): - tablename = "books" + ormar_config = base_ormar_config.copy(tablename="books") id: int = ormar.Integer(primary_key=True) author: Optional[Author] = ormar.ForeignKey(Author) @@ -224,10 +221,9 @@ class Book(ormar.Model): # create the database # note that in production you should use migrations # 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 -metadata.drop_all(engine) -metadata.create_all(engine) +base_ormar_config.metadata.drop_all(engine) +base_ormar_config.metadata.create_all(engine) # all functions below are divided into functionality categories diff --git a/docs/models/index.md b/docs/models/index.md index 4c3239350..05ebcdbb6 100644 --- a/docs/models/index.md +++ b/docs/models/index.md @@ -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. -```Python hl_lines="10" +```Python hl_lines="9" --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. -```Python hl_lines="15 16 17" +```Python hl_lines="15-17" --8<-- "../docs_src/models/docs001.py" ``` @@ -60,7 +60,7 @@ you should get back exactly same value in `response`.). !!!warning pydantic fields have to be always **Optional** and it cannot be changed (otherwise db load validation would fail) -```Python hl_lines="18" +```Python hl_lines="19" --8<-- "../docs_src/models/docs014.py" ``` @@ -73,11 +73,15 @@ Since it can be a function you can set `default=datetime.datetime.now` and get c ```python # <==related of 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 Meta: - tablename: str = "users2" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="users2") id: int = ormar.Integer(primary_key=True) email: str = ormar.String(max_length=255, nullable=False) @@ -90,7 +94,7 @@ class User(ormar.Model): ) # <==related of code removed for clarity==> -app =FastAPI() +app = FastAPI() @app.post("/users/") async def create_user(user: User): @@ -134,17 +138,17 @@ If for whatever reason you prefer to change the name in the database but keep th with specifying `name` parameter during Field declaration 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" ``` Note that you can also change the ForeignKey column name -```Python hl_lines="21" +```Python hl_lines="34" --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="28" +```Python hl_lines="43" --8<-- "../docs_src/models/docs010.py" ``` @@ -152,7 +156,7 @@ But for now you cannot change the ManyToMany column names as they go through oth 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 import ormar @@ -170,12 +174,10 @@ class MyQuerySetClass(QuerySet): class Book(ormar.Model): - - class Meta(ormar.ModelMeta): - metadata = metadata - database = database - tablename = "book" - queryset_class = MyQuerySetClass + ormar_config = base_ormar_config.copy( + queryset_class=MyQuerySetClass, + tablename="book", + ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=32) @@ -217,9 +219,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. -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" ``` @@ -231,9 +233,9 @@ Created instance needs to be passed to every `Model` with `Meta` class `database 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" ``` @@ -243,25 +245,22 @@ Created instance needs to be passed to every `Model` with `Meta` class `metadata #### Best practice -Only thing that `ormar` expects is a class with name `Meta` and two class variables: `metadata` and `databases`. +Only thing that `ormar` expects is a field with name `ormar_config`. -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. +So instead of providing the same parameters over and over again for all models +you should creat an object and use its copy in all models. -```Python hl_lines="14 20 33" +```Python hl_lines="9-11 18 27" --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'. -You can overwrite this parameter by providing `Meta` class `tablename` argument. +You can overwrite this parameter by providing `ormar_config` object `tablename` argument. -```Python hl_lines="12 13 14" +```Python hl_lines="14-16" --8<-- "../docs_src/models/docs002.py" ``` @@ -279,9 +278,9 @@ Right now only `IndexColumns` and `UniqueColumns` constraints are supported. #### 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" ``` @@ -292,9 +291,9 @@ You can set this parameter by providing `Meta` class `constraints` argument. #### 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" ``` @@ -305,9 +304,9 @@ You can set this parameter by providing `Meta` class `constraints` argument. #### 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" ``` @@ -319,9 +318,9 @@ You can set this parameter by providing `Meta` class `constraints` argument. 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 To read more about available settings visit the [pydantic](https://pydantic-docs.helpmanual.io/usage/model_config/) config page. @@ -330,13 +329,11 @@ Note that if you do not provide your own configuration, ormar will do it for you The default config provided is as follows: ```python -class Config(pydantic.BaseConfig): - orm_mode = True - validate_assignment = True +model_config = ConfigDict(validate_assignment=True) ``` 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" ``` @@ -348,69 +345,62 @@ 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. -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`. Note that `ormar` does not allow accepting extra fields, you can only ignore them or forbid them (raise exception if present) ```python -from ormar import Extra +from ormar import Extra, OrmarConfig class Child(ormar.Model): - class Meta(ormar.ModelMeta): - tablename = "children" - metadata = metadata - database = database - extra = Extra.ignore # set extra setting to prevent exceptions on extra fields presence + ormar_config = OrmarConfig( + tablename="children", + extra=Extra.ignore # set extra setting to prevent exceptions on extra fields presence + ) 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) ``` -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 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` -parameter to model `Meta` class. +parameter to model `ormar_config` object. Sample default ordering: ```python -database = databases.Database(DATABASE_URL) -metadata = sqlalchemy.MetaData() - +base_ormar_config = ormar.OrmarConfig( + database=databases.Database(DATABASE_URL), + metadata=sqlalchemy.MetaData(), +) -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database # default sort by column id ascending class Author(ormar.Model): - class Meta(BaseMeta): - tablename = "authors" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) ``` Modified ```python - -database = databases.Database(DATABASE_URL) -metadata = sqlalchemy.MetaData() - - -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database +base_ormar_config = ormar.OrmarConfig( + database=databases.Database(DATABASE_URL), + metadata=sqlalchemy.MetaData(), +) # now default sort by name descending class Author(ormar.Model): - class Meta(BaseMeta): - tablename = "authors" - orders_by = ["-name"] + ormar_config = base_ormar_config.copy( + orders_by = ["-name"], + tablename="authors", + ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -425,7 +415,7 @@ There are two ways to create and persist the `Model` instance in the database. 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" ``` @@ -435,7 +425,7 @@ For creating multiple objects at once a `bulk_create()` QuerySet's method is ava Each model has a `QuerySet` initialised as `objects` parameter -```Python hl_lines="23" +```Python hl_lines="28" --8<-- "../docs_src/models/docs007.py" ``` diff --git a/docs/models/inheritance.md b/docs/models/inheritance.md index 0f4294cd2..36695bd9d 100644 --- a/docs/models/inheritance.md +++ b/docs/models/inheritance.md @@ -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. ```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 class AuditMixin: created_by: str = ormar.String(max_length=100) @@ -45,10 +52,7 @@ class DateFieldsMixins: # a models can inherit from one or more mixins class Category(ormar.Model, DateFieldsMixins, AuditMixin): - class Meta(ormar.ModelMeta): - tablename = "categories" - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=50, unique=True, index=True) @@ -57,7 +61,7 @@ class Category(ormar.Model, DateFieldsMixins, AuditMixin): !!!tip 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`, `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. !!!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. 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 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 # since this model will never be initialized you can skip metadata and database class AuditModel(ormar.Model): - class Meta: - abstract = True + ormar_config = base_ormar_config.copy(abstract=True) created_by: str = ormar.String(max_length=100) 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 class DateFieldsModel(ormar.Model): - class Meta: - abstract = True - metadata = metadata - database = db + ormar_config = base_ormar_config.copy( + abstract=True, + metadata=metadata, + database=db, + ) created_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 class Category(DateFieldsModel, AuditModel): - class Meta(ormar.ModelMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=50, unique=True, index=True) @@ -143,12 +146,13 @@ the previously defined one. ```python # base class class DateFieldsModel(ormar.Model): - class Meta: - abstract = True - metadata = metadata - database = db + ormar_config = OrmarConfig( + abstract=True, + metadata=metadata, + database=db, # 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( default=datetime.datetime.now, name="creation_date" @@ -159,10 +163,11 @@ class DateFieldsModel(ormar.Model): class RedefinedField(DateFieldsModel): - class Meta(ormar.ModelMeta): - tablename = "redefines" - metadata = metadata - database = db + ormar_config = OrmarConfig( + tablename="redefines", + metadata=metadata, + database=db, + ) id: int = ormar.Integer(primary_key=True) # here the created_date is replaced by the String field @@ -170,12 +175,12 @@ class RedefinedField(DateFieldsModel): # 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.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( - RedefinedField.Meta.table.columns["creation_date"].type, + RedefinedField.ormar_config.table.columns["creation_date"].type, sqlalchemy.sql.sqltypes.String, ) ``` @@ -225,9 +230,7 @@ That might sound complicated but let's look at the following example: ```python # normal model used in relation class Person(ormar.Model): - class Meta: - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -235,10 +238,7 @@ class Person(ormar.Model): # parent model - needs to be abstract class Car(ormar.Model): - class Meta: - abstract = True - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(abstract=True) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=50) @@ -249,16 +249,13 @@ class Car(ormar.Model): class Truck(Car): - class Meta: - pass + ormar_config = base_ormar_config.copy() max_capacity: int = ormar.Integer() class Bus(Car): - class Meta: - # default naming is name.lower()+'s' so it's ugly for buss ;) - tablename = "buses" + ormar_config = base_ormar_config.copy(tablename="buses") max_persons: int = ormar.Integer() ``` @@ -266,7 +263,7 @@ class Bus(Car): Now when you will inspect the fields on Person model you will get: ```python -Person.Meta.model_fields +Person.ormar_config.model_fields """ {'id': , 'name': , @@ -293,8 +290,7 @@ different `related_name` parameter. ```python # rest of the above example remains the same class Bus(Car): - class Meta: - tablename = "buses" + ormar_config = base_ormar_config.copy(tablename="buses") # new field that changes the related_name owner: Person = ormar.ForeignKey(Person, related_name="buses") @@ -304,7 +300,7 @@ class Bus(Car): Now the columns looks much better. ```python -Person.Meta.model_fields +Person.ormar_config.model_fields """ {'id': , 'name': , @@ -344,27 +340,18 @@ We will modify the previous example described above to use m2m relation for co_o ```python # person remain the same as above class Person(ormar.Model): - class Meta: - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) # new through model between Person and Car2 class PersonsCar(ormar.Model): - class Meta: - tablename = "cars_x_persons" - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(tablename="cars_x_persons") # note how co_owners is now ManyToMany relation class Car2(ormar.Model): - class Meta: - # parent class needs to be marked abstract - abstract = True - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(abstract=True) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=50) @@ -379,16 +366,13 @@ class Car2(ormar.Model): # child models define only additional Fields class Truck2(Car2): - class Meta: - # note how you don't have to provide inherited Meta params - tablename = "trucks2" + ormar_config = base_ormar_config.copy(tablename="trucks2") max_capacity: int = ormar.Integer() class Bus2(Car2): - class Meta: - tablename = "buses2" + ormar_config = base_ormar_config.copy(tablename="busses2") max_persons: int = ormar.Integer() ``` @@ -402,7 +386,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. ```python -Person.Meta.model_fields +Person.ormar_config.model_fields { # note how all relation fields need to be unique on Person # regardless if autogenerated or manually overwritten @@ -425,14 +409,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. ```python -Bus2.Meta.model_fields['co_owners'].through +Bus2.ormar_config.model_fields['co_owners'].through -Bus2.Meta.model_fields['co_owners'].through.Meta.tablename +Bus2.ormar_config.model_fields['co_owners'].through.ormar_config.tablename 'cars_x_persons_buses2' -Truck2.Meta.model_fields['co_owners'].through +Truck2.ormar_config.model_fields['co_owners'].through -Truck2.Meta.model_fields['co_owners'].through.Meta.tablename +Truck2.ormar_config.model_fields['co_owners'].through.ormar_config.tablename 'cars_x_persons_trucks2' ``` @@ -443,7 +427,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: ```python -Bus2.Meta.metadata.tables.keys() +Bus2.ormar_config.metadata.tables.keys() dict_keys(['test_date_models', 'categories', 'subjects', 'persons', 'trucks', 'buses', 'cars_x_persons_trucks2', 'trucks2', 'cars_x_persons_buses2', 'buses2']) ``` @@ -469,26 +453,24 @@ Ormar allows you to skip certain fields in inherited model that are coming from !!!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. -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 -metadata = sa.MetaData() -db = databases.Database(DATABASE_URL) +base_ormar_config = OrmarConfig( + metadata=sa.MetaData(), + database=databases.Database(DATABASE_URL), +) class AuditModel(ormar.Model): - class Meta: - abstract = True + ormar_config = base_ormar_config.copy(abstract=True) created_by: str = ormar.String(max_length=100) updated_by: str = ormar.String(max_length=100, default="Sam") class DateFieldsModel(ormar.Model): - class Meta(ormar.ModelMeta): - abstract = True - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(abstract=True) created_date: datetime.datetime = ormar.DateTime( default=datetime.datetime.now, name="creation_date" @@ -499,10 +481,11 @@ class DateFieldsModel(ormar.Model): class Category(DateFieldsModel, AuditModel): - class Meta(ormar.ModelMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy( + tablename="categories", # 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) name: str = ormar.String(max_length=50, unique=True, index=True) @@ -523,38 +506,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: ```python -metadata = sa.MetaData() -db = databases.Database(DATABASE_URL) +base_ormar_config = OrmarConfig( + metadata=sa.MetaData(), + database=databases.Database(DATABASE_URL), +) class AuditCreateModel(ormar.Model): - class Meta: - abstract = True + ormar_config = base_ormar_config.copy(abstract=True) created_by: str = ormar.String(max_length=100) class AuditUpdateModel(ormar.Model): - class Meta: - abstract = True + ormar_config = base_ormar_config.copy(abstract=True) updated_by: str = ormar.String(max_length=100, default="Sam") class CreateDateFieldsModel(ormar.Model): - class Meta(ormar.ModelMeta): - abstract = True - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(abstract=True) created_date: datetime.datetime = ormar.DateTime( default=datetime.datetime.now, name="creation_date" ) class UpdateDateFieldsModel(ormar.Model): - class Meta(ormar.ModelMeta): - abstract = True - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(abstract=True) updated_date: datetime.datetime = ormar.DateTime( default=datetime.datetime.now, name="modification_date" @@ -562,8 +539,7 @@ class UpdateDateFieldsModel(ormar.Model): class Category(CreateDateFieldsModel, AuditCreateModel): - class Meta(ormar.ModelMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=50, unique=True, index=True) diff --git a/docs/models/internals.md b/docs/models/internals.md index 463d368fa..358c31957 100644 --- a/docs/models/internals.md +++ b/docs/models/internals.md @@ -20,27 +20,27 @@ For example to list pydantic model fields you can: ## 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: -```Python hl_lines="20" +```Python hl_lines="24" --8<-- "../docs_src/models/docs004.py" ``` !!!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 For more options visit official [sqlalchemy-metadata][sqlalchemy-metadata] documentation. ## 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: -```Python hl_lines="20" +```Python hl_lines="22" --8<-- "../docs_src/models/docs005.py" ``` diff --git a/docs/models/methods.md b/docs/models/methods.md index f3f4dd1bb..13fb33e36 100644 --- a/docs/models/methods.md +++ b/docs/models/methods.md @@ -96,10 +96,7 @@ Flag indicates whether fields which were not explicitly set when creating the mo ```python class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, default="Test") @@ -107,10 +104,7 @@ class Category(ormar.Model): class Item(ormar.Model): - class Meta: - tablename = "items" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -140,20 +134,14 @@ Flag indicates are equal to their default values (whether set or otherwise) shou ```python class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, default="Test") visibility: bool = ormar.Boolean(default=True) class Item(ormar.Model): - class Meta: - tablename = "items" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -181,10 +169,7 @@ Flag indicates whether fields which are equal to `None` should be excluded from ```python class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=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 Meta: - tablename = "items" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -226,10 +208,7 @@ Setting flag to `True` will exclude all primary key columns in a tree, including ```python class Item(ormar.Model): - class Meta: - tablename = "items" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -249,20 +228,14 @@ Setting the `exclude_through_models=True` will exclude all through models, inclu ```python class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Item(ormar.Model): - class Meta: - tablename = "items" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -321,25 +294,21 @@ That means that this way you can effortlessly create pydantic models for request Given sample ormar models like follows: ```python -metadata = sqlalchemy.MetaData() -database = databases.Database(DATABASE_URL, force_rollback=True) +base_ormar_config = ormar.OrmarConfig( + metadata=sqlalchemy.MetaData(), + database=databases.Database(DATABASE_URL, force_rollback=True), +) -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database - class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Item(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, default="test") @@ -482,10 +451,7 @@ In example: ```python class Movie(ormar.Model): - class Meta: - tablename = "movies" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, nullable=False, name="title") @@ -584,18 +550,14 @@ Example: ```python class Department(ormar.Model): - class Meta: - database = database - metadata = metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) department_name: str = ormar.String(max_length=100) class Course(ormar.Model): - class Meta: - database = database - metadata = metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) course_name: str = ormar.String(max_length=100) @@ -604,9 +566,7 @@ class Course(ormar.Model): class Student(ormar.Model): - class Meta: - database = database - metadata = metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) diff --git a/docs/models/migrations.md b/docs/models/migrations.md index 684101456..7577a682e 100644 --- a/docs/models/migrations.md +++ b/docs/models/migrations.md @@ -16,14 +16,14 @@ engine = sqlalchemy.create_engine("sqlite:///test.db") 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 import sqlalchemy # get your database url in sqlalchemy format - same as used with databases instance used in Model definition engine = sqlalchemy.create_engine("sqlite:///test.db") # Artist is an ormar model from previous examples -Artist.Meta.table.create(engine) +Artist.ormar_config.table.create(engine) ``` !!!warning diff --git a/docs/queries/aggregations.md b/docs/queries/aggregations.md index 7a2f9e81c..6294754ff 100644 --- a/docs/queries/aggregations.md +++ b/docs/queries/aggregations.md @@ -32,10 +32,7 @@ the count will be the total number of rows returned ```python class Book(ormar.Model): - class Meta: - tablename = "books" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) title: str = ormar.String(max_length=200) @@ -60,10 +57,7 @@ Returns a bool value to confirm if there are rows matching the given criteria (a ```python class Book(ormar.Model): - class Meta: - tablename = "books" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) title: str = ormar.String(max_length=200) diff --git a/docs/queries/create.md b/docs/queries/create.md index 659e2e257..b10962f3a 100644 --- a/docs/queries/create.md +++ b/docs/queries/create.md @@ -30,10 +30,7 @@ The allowed kwargs are `Model` fields names and proper value types. ```python class Album(ormar.Model): - class Meta: - tablename = "album" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="album") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -68,10 +65,7 @@ i.e. `get_or_create(_defaults: {"title": "I win"}, title="never used")` will alw ```python class Album(ormar.Model): - class Meta: - tablename = "album" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="album") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -106,7 +100,7 @@ assert album == album2 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" ``` @@ -122,7 +116,7 @@ Allows you to create multiple objects at once. 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" ``` diff --git a/docs/queries/delete.md b/docs/queries/delete.md index aec5171a1..01f2c93fb 100644 --- a/docs/queries/delete.md +++ b/docs/queries/delete.md @@ -26,7 +26,7 @@ If you do not provide this flag or a filter a `QueryDefinitionError` will be rai Return number of rows deleted. -```python hl_lines="26-30" +```python hl_lines="40-44" --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 class Album(ormar.Model): - class Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) is_best_seller: bool = ormar.Boolean(default=False) class Track(ormar.Model): - class Meta: - tablename = "tracks" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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 class Album(ormar.Model): - class Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) is_best_seller: bool = ormar.Boolean(default=False) class Track(ormar.Model): - class Meta: - tablename = "tracks" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -148,4 +136,4 @@ await album.tracks.clear() await album.tracks.clear(keep_reversed=False) ``` -[querysetproxy]: ../relations/queryset-proxy.md \ No newline at end of file +[querysetproxy]: ../relations/queryset-proxy.md diff --git a/docs/queries/filter-and-sort.md b/docs/queries/filter-and-sort.md index 7d9637f4b..1888a6b17 100644 --- a/docs/queries/filter-and-sort.md +++ b/docs/queries/filter-and-sort.md @@ -35,20 +35,14 @@ a filter across an FK relationship. ```python class Album(ormar.Model): - class Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) is_best_seller: bool = ormar.Boolean(default=False) class Track(ormar.Model): - class Meta: - tablename = "tracks" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -197,20 +191,14 @@ conditions. ```python class Album(ormar.Model): - class Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) is_best_seller: bool = ormar.Boolean(default=False) class Track(ormar.Model): - class Meta: - tablename = "tracks" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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: ```python -database = databases.Database(DATABASE_URL) -metadata = sqlalchemy.MetaData() - - -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database +base_ormar_config = ormar.OrmarConfig( + database=databases.Database(DATABASE_URL), + metadata=sqlalchemy.MetaData(), +) class Author(ormar.Model): - class Meta(BaseMeta): - tablename = "authors" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Book(ormar.Model): - class Meta(BaseMeta): - tablename = "books" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) author: Optional[Author] = ormar.ForeignKey(Author) @@ -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` 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 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 * Relation passed `orders_by` and `related_orders_by` if exists - * Model `Meta` class `orders_by` - * Model `primary_key` column ascending (fallback, used if none of above provided) + * Model's `ormar_config` object `orders_by` + * 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).** diff --git a/docs/queries/joins-and-subqueries.md b/docs/queries/joins-and-subqueries.md index cd840e827..bc0b0a06a 100644 --- a/docs/queries/joins-and-subqueries.md +++ b/docs/queries/joins-and-subqueries.md @@ -46,20 +46,14 @@ To chain related `Models` relation use double underscores between names. ```python class Album(ormar.Model): - class Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) is_best_seller: bool = ormar.Boolean(default=False) class Track(ormar.Model): - class Meta: - tablename = "tracks" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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 class SchoolClass(ormar.Model): - class Meta: - tablename = "schoolclasses" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="schoolclasses") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -93,20 +84,14 @@ class SchoolClass(ormar.Model): class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Student(ormar.Model): - class Meta: - tablename = "students" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -115,10 +100,7 @@ class Student(ormar.Model): class Teacher(ormar.Model): - class Meta: - tablename = "teachers" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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: ```python -database = databases.Database(DATABASE_URL, force_rollback=True) -metadata = sqlalchemy.MetaData() - - -class BaseMeta(ormar.ModelMeta): - database = database - metadata = metadata +base_ormar_config = OrmarConfig( + database=databases.Database(DATABASE_URL, force_rollback=True), + metadata=sqlalchemy.MetaData(), +) class Address(ormar.Model): - class Meta(BaseMeta): - tablename = "addresses" + ormar_config = base_ormar_config.copy(tablename="addresses") id: int = ormar.Integer(primary_key=True) street: str = ormar.String(max_length=100, nullable=False) @@ -203,8 +181,7 @@ class Address(ormar.Model): class Branch(ormar.Model): - class Meta(BaseMeta): - tablename = "branches" + ormar_config = base_ormar_config.copy(tablename="branches") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100, nullable=False) @@ -212,8 +189,7 @@ class Branch(ormar.Model): class Company(ormar.Model): - class Meta(BaseMeta): - tablename = "companies" + ormar_config = base_ormar_config.copy(tablename="companies") id: int = ormar.Integer(primary_key=True) 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 class Album(ormar.Model): - class Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) is_best_seller: bool = ormar.Boolean(default=False) class Track(ormar.Model): - class Meta: - tablename = "tracks" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -301,10 +271,7 @@ You can provide a string, or a list of strings ```python class SchoolClass(ormar.Model): - class Meta: - tablename = "schoolclasses" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="schoolclasses") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -312,20 +279,14 @@ class SchoolClass(ormar.Model): class Category(ormar.Model): - class Meta: - tablename = "categories" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Student(ormar.Model): - class Meta: - tablename = "students" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -334,10 +295,7 @@ class Student(ormar.Model): class Teacher(ormar.Model): - class Meta: - tablename = "teachers" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) diff --git a/docs/queries/pagination-and-rows-number.md b/docs/queries/pagination-and-rows-number.md index 21f41912d..c810ac6cb 100644 --- a/docs/queries/pagination-and-rows-number.md +++ b/docs/queries/pagination-and-rows-number.md @@ -22,10 +22,7 @@ Combines the `offset` and `limit` methods based on page number and size ```python class Track(ormar.Model): - class Meta: - tablename = "track" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="track") id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -52,10 +49,7 @@ use the `limit_raw_sql` parameter flag, and set it to `True`. ```python class Track(ormar.Model): - class Meta: - tablename = "track" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="track") id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -86,10 +80,7 @@ use the `limit_raw_sql` parameter flag, and set it to `True`. ```python class Track(ormar.Model): - class Meta: - tablename = "track" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="track") id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) diff --git a/docs/queries/raw-data.md b/docs/queries/raw-data.md index 57e027356..00941fc47 100644 --- a/docs/queries/raw-data.md +++ b/docs/queries/raw-data.md @@ -40,8 +40,7 @@ Example: # declared models class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=40) @@ -49,8 +48,7 @@ class Category(ormar.Model): class Post(ormar.Model): - class Meta(BaseMeta): - tablename = "posts" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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 class User(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Role(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -216,8 +212,7 @@ Example: # declared models class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=40) @@ -225,8 +220,7 @@ class Category(ormar.Model): class Post(ormar.Model): - class Meta(BaseMeta): - tablename = "posts" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=200) @@ -257,8 +251,7 @@ Let's complicate the relation and modify the previously mentioned Category model ```python class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=40) diff --git a/docs/queries/read.md b/docs/queries/read.md index 018d69311..295234b14 100644 --- a/docs/queries/read.md +++ b/docs/queries/read.md @@ -31,10 +31,7 @@ Passing a criteria is actually calling filter(*args, **kwargs) method described ```python class Track(ormar.Model): - class Meta: - tablename = "track" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="track") id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -74,10 +71,7 @@ a new one with given kwargs and _defaults. ```python class Album(ormar.Model): - class Meta: - tablename = "album" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="album") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -114,10 +108,7 @@ Gets the first row from the db ordered by primary key column ascending. ```python class Album(ormar.Model): - class Meta: - tablename = "album" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="album") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -143,20 +134,14 @@ If there are no rows meeting the criteria an empty list is returned. ```python class Album(ormar.Model): - class Meta: - tablename = "album" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="album") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Track(ormar.Model): - class Meta: - tablename = "track" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="track") id: int = ormar.Integer(primary_key=True) album: Optional[Album] = ormar.ForeignKey(Album) @@ -186,10 +171,7 @@ If there are no rows meeting the criteria an empty async generator is returned. ```python class Album(ormar.Model): - class Meta: - tablename = "album" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="album") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) diff --git a/docs/queries/select-columns.md b/docs/queries/select-columns.md index d5c6c765d..2185c4b27 100644 --- a/docs/queries/select-columns.md +++ b/docs/queries/select-columns.md @@ -35,10 +35,7 @@ metadata = sqlalchemy.MetaData() class Company(ormar.Model): - class Meta: - tablename = "companies" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="companies") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -46,10 +43,7 @@ class Company(ormar.Model): class Car(ormar.Model): - class Meta: - tablename = "cars" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) manufacturer = ormar.ForeignKey(Company) @@ -208,15 +202,14 @@ import sqlalchemy import ormar from tests.settings import DATABASE_URL -database = databases.Database(DATABASE_URL, force_rollback=True) -metadata = sqlalchemy.MetaData() +base_ormar_config = ormar.OrmarConfig( + database=databases.Database(DATABASE_URL, force_rollback=True), + metadata=sqlalchemy.MetaData(), +) class Company(ormar.Model): - class Meta: - tablename = "companies" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy(tablename="companies") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -224,10 +217,7 @@ class Company(ormar.Model): class Car(ormar.Model): - class Meta: - tablename = "cars" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) manufacturer = ormar.ForeignKey(Company) diff --git a/docs/queries/update.md b/docs/queries/update.md index 642f04407..f9f00e901 100644 --- a/docs/queries/update.md +++ b/docs/queries/update.md @@ -29,7 +29,7 @@ If you do not provide this flag or a filter a `QueryDefinitionError` will be rai Return number of rows updated. -```Python hl_lines="26-28" +```Python hl_lines="42-44" --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. -```Python hl_lines="26-32" +```Python hl_lines="40-48" --8<-- "../docs_src/queries/docs003.py" ``` @@ -123,4 +123,4 @@ from other side of the relation. [querysetproxy]: ../relations/queryset-proxy.md [models-upsert]: ../models/methods.md#upsert -[models-save-related]: ../models/methods.md#save_related \ No newline at end of file +[models-save-related]: ../models/methods.md#save_related diff --git a/docs/relations/foreign-key.md b/docs/relations/foreign-key.md index 2f880a1e9..5860ad2dc 100644 --- a/docs/relations/foreign-key.md +++ b/docs/relations/foreign-key.md @@ -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`. -```Python hl_lines="29" +```Python hl_lines="30" --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: -```Python hl_lines="29 35" +```Python hl_lines="29 36" --8<-- "../docs_src/fields/docs001.py" ``` @@ -52,8 +52,7 @@ Example: ```python class Author(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) first_name: str = ormar.String(max_length=80) @@ -61,8 +60,7 @@ class Author(ormar.Model): class Post(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) title: str = ormar.String(max_length=200) @@ -175,7 +173,7 @@ To read which methods of QuerySet are available read below [querysetproxy][query But you can overwrite this 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" ``` @@ -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. -```Python hl_lines="34-35" +```Python hl_lines="35-36" --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. -```Python hl_lines="37-38" +```Python hl_lines="38-39" --8<-- "../docs_src/relations/docs001.py" ``` @@ -248,7 +246,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 `model_dump()` method. -```Python hl_lines="40-41" +```Python hl_lines="41-42" --8<-- "../docs_src/relations/docs001.py" ``` @@ -256,7 +254,7 @@ You can build the dictionary yourself or get it from existing model with `model_ 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" ``` @@ -283,4 +281,4 @@ Finally you can explicitly set it to None (default behavior if no value passed). [fields]: ./queries.md#fields [exclude_fields]: ./queries.md#exclude_fields [order_by]: ./queries.md#order_by -[server_default]: ../fields/common-parameters.md#server-default \ No newline at end of file +[server_default]: ../fields/common-parameters.md#server-default diff --git a/docs/relations/index.md b/docs/relations/index.md index 385516a8e..979e766e5 100644 --- a/docs/relations/index.md +++ b/docs/relations/index.md @@ -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. -```Python hl_lines="17" +```Python hl_lines="26" --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. -So in relation ato example above. +So in relation to example above. -```Python hl_lines="17" +```Python hl_lines="8" class Department(ormar.Model): - class Meta: - database = database - metadata = metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -54,19 +52,20 @@ To define many-to-many relation use `ManyToMany` field. ```python hl_lines="18" class Category(ormar.Model): - class Meta: - tablename = "categories" - database = database - metadata = metadata + ormar_config = ormar.OrmarConfig( + database=database, + metadata=metadata, + tablename="categories", + ) id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=40) class Post(ormar.Model): - class Meta: - tablename = "posts" - database = database - metadata = metadata + ormar_config = ormar.OrmarConfig( + database=database, + metadata=metadata, + ) id: int = ormar.Integer(primary_key=True) 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. 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-20" class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = ormar.OrmarConfig( + database=database, + metadata=metadata, + tablename="categories", + ) id = ormar.Integer(primary_key=True) name = ormar.String(max_length=40) # you can specify additional fields on through model class PostCategory(ormar.Model): - class Meta(BaseMeta): - tablename = "posts_x_categories" + ormar_config = ormar.OrmarConfig( + database=database, + metadata=metadata, + tablename="posts_x_categories", + ) id: int = ormar.Integer(primary_key=True) sort_order: int = ormar.Integer(nullable=True) @@ -111,8 +116,10 @@ class PostCategory(ormar.Model): class Post(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = ormar.OrmarConfig( + database=database, + metadata=metadata, + ) id: int = ormar.Integer(primary_key=True) title: str = ormar.String(max_length=200) @@ -130,7 +137,7 @@ class Post(ormar.Model): ## 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 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: -```python hl_lines="24" +```python hl_lines="23" database = databases.Database(DATABASE_URL) metadata = sqlalchemy.MetaData() -class BaseMeta(ormar.ModelMeta): - metadata = metadata - database = database +base_ormar_config = ormar.OrmarConfig( + database=database, + metadata=metadata, +) class Author(ormar.Model): - class Meta(BaseMeta): - tablename = "authors" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Book(ormar.Model): - class Meta(BaseMeta): - tablename = "books" + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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 `ForwardRef` from `typing` module. -```python hl_lines="1 11 14" +```python hl_lines="1 9 12" PersonRef = ForwardRef("Person") class Person(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -210,4 +214,4 @@ Person.update_forward_refs() [foreign-keys]: ./foreign-key.md [many-to-many]: ./many-to-many.md [queryset-proxy]: ./queryset-proxy.md -[postponed-annotations]: ./postponed-annotations.md \ No newline at end of file +[postponed-annotations]: ./postponed-annotations.md diff --git a/docs/relations/many-to-many.md b/docs/relations/many-to-many.md index ae231e984..de9bc108e 100644 --- a/docs/relations/many-to-many.md +++ b/docs/relations/many-to-many.md @@ -9,7 +9,7 @@ Sqlalchemy column and Type are automatically taken from target `Model`. ## Defining Models -```Python hl_lines="40" +```Python hl_lines="34" --8<-- "../docs_src/relations/docs002.py" ``` @@ -28,16 +28,14 @@ By default it's child (source) `Model` name + s, like courses in snippet below: ```python class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=40) class Post(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) title: str = ormar.String(max_length=200) @@ -96,16 +94,14 @@ Example: ```python class Category(ormar.Model): - class Meta(BaseMeta): - tablename = "categories" + ormar_config = base_ormar_config.copy(tablename="categories") id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=40) class Post(ormar.Model): - class Meta(BaseMeta): - pass + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) title: str = ormar.String(max_length=200) @@ -141,7 +137,7 @@ assert len(categories) == 1 Optionally if you want to add additional fields you can explicitly create and pass the through model class. -```Python hl_lines="14-20 29" +```Python hl_lines="19-24 32" --8<-- "../docs_src/relations/docs004.py" ``` @@ -170,9 +166,7 @@ So in example like this: ```python ... # course declaration omitted class Student(ormar.Model): - class Meta: - database = database - metadata = metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -180,10 +174,7 @@ class Student(ormar.Model): # will produce default Through model like follows (example simplified) class StudentCourse(ormar.Model): - class Meta: - database = database - metadata = metadata - tablename = "students_courses" + ormar_config = base_ormar_config.copy(tablename="students_courses") id: int = ormar.Integer(primary_key=True) student = ormar.ForeignKey(Student) # default name @@ -199,10 +190,14 @@ Example: ```python ... # course declaration omitted +base_ormar_config = ormar.OrmarConfig( + database=databases.Database("sqlite:///db.sqlite"), + metadata=sqlalchemy.MetaData(), +) + + class Student(ormar.Model): - class Meta: - database = database - metadata = metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -212,10 +207,7 @@ class Student(ormar.Model): # will produce Through model like follows (example simplified) class StudentCourse(ormar.Model): - class Meta: - database = database - metadata = metadata - tablename = "students_courses" + ormar_config = base_ormar_config.copy(tablename="student_courses") id: int = ormar.Integer(primary_key=True) student_id = ormar.ForeignKey(Student) # set by through_relation_name @@ -238,7 +230,7 @@ so it's useful only when additional fields are provided on `Through` model. 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" ``` diff --git a/docs/relations/postponed-annotations.md b/docs/relations/postponed-annotations.md index e15629611..5907f8a34 100644 --- a/docs/relations/postponed-annotations.md +++ b/docs/relations/postponed-annotations.md @@ -36,9 +36,7 @@ PersonRef = ForwardRef("Person") class Person(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -72,9 +70,7 @@ PersonRef = ForwardRef("Person") class Person(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -93,14 +89,10 @@ and through parameters. ChildRef = ForwardRef("Child") class ChildFriend(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() class Child(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -132,9 +124,7 @@ TeacherRef = ForwardRef("Teacher") class Student(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -144,16 +134,11 @@ class Student(ormar.Model): class StudentTeacher(ormar.Model): - class Meta(ModelMeta): - tablename = 'students_x_teachers' - metadata = metadata - database = db + ormar_config = base_ormar_config.copy(tablename='students_x_teachers') class Teacher(ormar.Model): - class Meta(ModelMeta): - metadata = metadata - database = db + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) @@ -168,4 +153,4 @@ Student.update_forward_refs() !!!warning Remember that `related_name` needs to be unique across related models regardless - of how many relations are defined. \ No newline at end of file + of how many relations are defined. diff --git a/docs/relations/queryset-proxy.md b/docs/relations/queryset-proxy.md index fa70d6f41..90c89fbfb 100644 --- a/docs/relations/queryset-proxy.md +++ b/docs/relations/queryset-proxy.md @@ -127,7 +127,7 @@ provided Through model. Given sample like this: -```Python hl_lines="14-20 29" +```Python hl_lines="19-24 32" --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 name and a dictionary of fields. -```Python hl_lines="14-20 29" +```Python hl_lines="19-24 32" --8<-- "../docs_src/relations/docs004.py" ``` diff --git a/docs/signals.md b/docs/signals.md index 2c33eebdd..cdd7b7601 100644 --- a/docs/signals.md +++ b/docs/signals.md @@ -14,15 +14,15 @@ import sqlalchemy 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 Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) 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. -```Python hl_lines="1" +```Python hl_lines="7" --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. -```Python hl_lines="4-7" +```Python hl_lines="27-30" --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 queries inside your receivers if you want to. -```Python hl_lines="15-22" +```Python hl_lines="40-47" --8<-- "../docs_src/signals/docs002.py" ``` @@ -106,7 +106,7 @@ class AlbumAuditor: auditor = AlbumAuditor() pre_save(Album)(auditor.before_save) # 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 album = Album(name='Miami') album.signals.pre_save.connect(auditor.before_save) @@ -127,7 +127,7 @@ async def before_update(sender, instance, **kwargs): instance.is_best_seller = True # 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 album = Album(name='Miami') album.signals.pre_save.disconnect(before_save) @@ -251,23 +251,23 @@ import sqlalchemy 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 Meta: - tablename = "albums" - metadata = metadata - database = database + ormar_config = base_ormar_config.copy() 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) -Album.Meta.signals.your_custom_signal = ormar.Signal() -Album.Meta.signals.your_custom_signal.connect(your_receiver_name) +Album.ormar_config.signals.your_custom_signal = ormar.Signal() +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 @@ -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. ``` -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. ```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. @@ -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. ```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) ``` diff --git a/docs/transactions.md b/docs/transactions.md index b764192ca..d4d28a250 100644 --- a/docs/transactions.md +++ b/docs/transactions.md @@ -15,10 +15,10 @@ async with database.transaction(): ``` !!!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`. -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. ```python @@ -26,24 +26,25 @@ import databases import sqlalchemy 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 Meta: - database=database - metadata=metadata + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=255) # database is accessible from class -database = Author.Meta.database +database = Author.ormar_config.database # as well as from instance 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: diff --git a/docs_src/signals/docs002.py b/docs_src/signals/docs002.py index fd42ed337..0795d67fb 100644 --- a/docs_src/signals/docs002.py +++ b/docs_src/signals/docs002.py @@ -9,7 +9,8 @@ DATABASE_URL = "sqlite:///test.db" ormar_base_config = ormar.OrmarConfig( - database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData() + database=databases.Database(DATABASE_URL), + metadata=sqlalchemy.MetaData(), ) diff --git a/examples/script_from_readme.py b/examples/script_from_readme.py index b50d2dc5e..e00db6516 100644 --- a/examples/script_from_readme.py +++ b/examples/script_from_readme.py @@ -6,16 +6,15 @@ import sqlalchemy DATABASE_URL = "sqlite:///db.sqlite" -database = databases.Database(DATABASE_URL) -metadata = sqlalchemy.MetaData() # note that this step is optional -> all ormar cares is an individual # OrmarConfig for each of the models, but this way you do not # have to repeat the same parameters if you use only one database base_ormar_config = ormar.OrmarConfig( - metadata=metadata, - database=database, + database=databases.Database(DATABASE_URL), + metadata=sqlalchemy.MetaData(), + engine=sqlalchemy.create_engine(DATABASE_URL), ) # Note that all type hints are optional @@ -28,15 +27,14 @@ class Author(ormar.Model): - ormar_config = base_ormar_config.copy(tablename="authors") + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) class Book(ormar.Model): - - ormar_config = base_ormar_config.copy(tablename="books") + ormar_config = base_ormar_config.copy() id: int = ormar.Integer(primary_key=True) author: Optional[Author] = ormar.ForeignKey(Author) @@ -47,10 +45,9 @@ class Book(ormar.Model): # create the database # note that in production you should use migrations # 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 -metadata.drop_all(engine) -metadata.create_all(engine) +base_ormar_config.metadata.drop_all(base_ormar_config.engine) +base_ormar_config.metadata.create_all(base_ormar_config.engine) # all functions below are divided into functionality categories @@ -378,7 +375,7 @@ async def raw_data(): async def with_connect(function): # note that for any other backend than sqlite you actually need to # connect to the database to perform db operations - async with database: + async with base_ormar_config.database: await function() # note that if you use framework like `fastapi` you shouldn't connect @@ -408,4 +405,4 @@ async def with_connect(function): asyncio.run(with_connect(func)) # drop the database tables -metadata.drop_all(engine) +base_ormar_config.metadata.drop_all(base_ormar_config.engine)