From 1cca385b5f2f932596bb8831261bdf9c96780e65 Mon Sep 17 00:00:00 2001 From: collerek Date: Tue, 2 Feb 2021 11:57:37 +0100 Subject: [PATCH] fix coverage, bump version, update relaese, update docs --- docs/api/models/helpers/models.md | 31 ++++++++++- docs/api/models/model.md | 2 + docs/releases.md | 4 ++ ormar/__init__.py | 2 +- tests/test_cascades.py | 93 ++++++++++++++++++++++++++++++- tests/test_model_definition.py | 30 +++++++++- 6 files changed, 158 insertions(+), 4 deletions(-) diff --git a/docs/api/models/helpers/models.md b/docs/api/models/helpers/models.md index ee3b100..b3100c6 100644 --- a/docs/api/models/helpers/models.md +++ b/docs/api/models/helpers/models.md @@ -38,7 +38,36 @@ Current options are: **Arguments**: - `new_model (Model class)`: newly constructed Model -- `model_fields (Union[Dict[str, type], Dict])`: +- `model_fields (Union[Dict[str, type], Dict])`: dict of model fields + + +#### substitue\_backend\_pool\_for\_sqlite + +```python +substitue_backend_pool_for_sqlite(new_model: Type["Model"]) -> None +``` + +Recreates Connection pool for sqlite3 with new factory that +executes "PRAGMA foreign_keys=1; on initialization to enable foreign keys. + +**Arguments**: + +- `new_model (Model class)`: newly declared ormar Model + + +#### check\_required\_meta\_parameters + +```python +check_required_meta_parameters(new_model: Type["Model"]) -> None +``` + +Verifies if ormar.Model has database and metadata set. + +Recreates Connection pool for sqlite3 + +**Arguments**: + +- `new_model (Model class)`: newly declared ormar Model #### extract\_annotations\_and\_default\_vals diff --git a/docs/api/models/model.md b/docs/api/models/model.md index 9bf3a90..e78825f 100644 --- a/docs/api/models/model.md +++ b/docs/api/models/model.md @@ -30,6 +30,8 @@ nested models in result. **Arguments**: +- `current_relation_str (str)`: name of the relation field +- `source_model (Type[Model])`: model on which relation was defined - `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database - `select_related (List)`: list of names of related models fetched from database - `related_models (Union[List, Dict])`: list or dict of related models diff --git a/docs/releases.md b/docs/releases.md index fb7f0e2..457a048 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -21,6 +21,10 @@ for sqlite backend, meaning that each querry is run with a new connection and th This is changed in `ormar` since >=0.9.0 and by default each sqlite3 query has `"PRAGMA foreign_keys=1;"` run so now each sqlite3 connection by default enforces ForeignKey constraints including cascades. +## Other + +* Update api docs. +* Add tests for fk creation in db and for cascades in db # 0.8.1 diff --git a/ormar/__init__.py b/ormar/__init__.py index 40da69b..0c84957 100644 --- a/ormar/__init__.py +++ b/ormar/__init__.py @@ -65,7 +65,7 @@ class UndefinedType: # pragma no cover Undefined = UndefinedType() -__version__ = "0.8.1" +__version__ = "0.9.0" __all__ = [ "Integer", "BigInteger", diff --git a/tests/test_cascades.py b/tests/test_cascades.py index d1c0e6c..c1e772b 100644 --- a/tests/test_cascades.py +++ b/tests/test_cascades.py @@ -11,6 +11,25 @@ database = databases.Database(DATABASE_URL) metadata = sqlalchemy.MetaData() +class Band(ormar.Model): + class Meta: + tablename = "bands" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + name: str = ormar.String(max_length=100) + + +class ArtistsBands(ormar.Model): + class Meta: + tablename = "artists_x_bands" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + + class Artist(ormar.Model): class Meta: tablename = "artists" @@ -19,6 +38,7 @@ class Artist(ormar.Model): id: int = ormar.Integer(primary_key=True) name: str = ormar.String(max_length=100) + bands = ormar.ManyToMany(Band, through=ArtistsBands) class Album(ormar.Model): @@ -52,8 +72,16 @@ def create_test_database(): metadata.drop_all(engine) +@pytest.fixture(scope="function") +async def cleanup(): + yield + async with database: + await Band.objects.delete(each=True) + await Artist.objects.delete(each=True) + + @pytest.mark.asyncio -async def test_simple_cascade(): +async def test_simple_cascade(cleanup): async with database: artist = await Artist(name="Dr Alban").save() await Album(name="Jamaica", artist=artist).save() @@ -63,3 +91,66 @@ async def test_simple_cascade(): albums = await Album.objects.all() assert len(albums) == 0 + + +@pytest.mark.asyncio +async def test_nested_cascade(cleanup): + async with database: + artist = await Artist(name="Dr Alban").save() + album = await Album(name="Jamaica", artist=artist).save() + await Track(title="Yuhu", album=album).save() + + await Artist.objects.delete(id=artist.id) + + artists = await Artist.objects.all() + assert len(artists) == 0 + + albums = await Album.objects.all() + assert len(albums) == 0 + + tracks = await Track.objects.all() + assert len(tracks) == 0 + + +@pytest.mark.asyncio +async def test_many_to_many_cascade(cleanup): + async with database: + artist = await Artist(name="Dr Alban").save() + band = await Band(name="Scorpions").save() + await artist.bands.add(band) + + check = await Artist.objects.select_related("bands").get() + assert check.bands[0].name == "Scorpions" + + await Artist.objects.delete(id=artist.id) + + artists = await Artist.objects.all() + assert len(artists) == 0 + + bands = await Band.objects.all() + assert len(bands) == 1 + + connections = await ArtistsBands.objects.all() + assert len(connections) == 0 + + +@pytest.mark.asyncio +async def test_reverse_many_to_many_cascade(cleanup): + async with database: + artist = await Artist(name="Dr Alban").save() + band = await Band(name="Scorpions").save() + await artist.bands.add(band) + + check = await Artist.objects.select_related("bands").get() + assert check.bands[0].name == "Scorpions" + + await Band.objects.delete(id=band.id) + + artists = await Artist.objects.all() + assert len(artists) == 1 + + connections = await ArtistsBands.objects.all() + assert len(connections) == 0 + + bands = await Band.objects.all() + assert len(bands) == 0 diff --git a/tests/test_model_definition.py b/tests/test_model_definition.py index aaf6b66..9db3761 100644 --- a/tests/test_model_definition.py +++ b/tests/test_model_definition.py @@ -118,6 +118,29 @@ def test_model_attribute_json_access(example): assert example.test_json == dict(aa=12) +def test_missing_metadata(): + with pytest.raises(ModelDefinitionError): + + class JsonSample2(ormar.Model): + class Meta: + tablename = "jsons2" + database = database + + id: int = ormar.Integer(primary_key=True) + test_json = ormar.JSON(nullable=True) + + +def test_missing_database(): + with pytest.raises(ModelDefinitionError): + + class JsonSample3(ormar.Model): + class Meta: + tablename = "jsons3" + + id: int = ormar.Integer(primary_key=True) + test_json = ormar.JSON(nullable=True) + + def test_non_existing_attr(example): with pytest.raises(ValueError): example.new_attr = 12 @@ -143,12 +166,13 @@ def test_sqlalchemy_table_is_created(example): @typing.no_type_check -def test_no_pk_in_model_definition(): # type: ignore +def test_no_pk_in_model_definition(): with pytest.raises(ModelDefinitionError): # type: ignore class ExampleModel2(Model): # type: ignore class Meta: tablename = "example2" + database = database metadata = metadata test_string: str = ormar.String(max_length=250) # type: ignore @@ -162,6 +186,7 @@ def test_two_pks_in_model_definition(): class ExampleModel2(Model): class Meta: tablename = "example3" + database = database metadata = metadata id: int = ormar.Integer(primary_key=True) @@ -175,6 +200,7 @@ def test_setting_pk_column_as_pydantic_only_in_model_definition(): class ExampleModel2(Model): class Meta: tablename = "example4" + database = database metadata = metadata test: int = ormar.Integer(primary_key=True, pydantic_only=True) @@ -187,6 +213,7 @@ def test_decimal_error_in_model_definition(): class ExampleModel2(Model): class Meta: tablename = "example5" + database = database metadata = metadata test: decimal.Decimal = ormar.Decimal(primary_key=True) @@ -199,6 +226,7 @@ def test_string_error_in_model_definition(): class ExampleModel2(Model): class Meta: tablename = "example6" + database = database metadata = metadata test: str = ormar.String(primary_key=True)