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)