split tests into packages

This commit is contained in:
collerek
2021-03-30 12:48:39 +02:00
parent f0023773e3
commit da05e5ba1d
85 changed files with 5 additions and 4 deletions

View File

View File

@ -0,0 +1,156 @@
from typing import Optional
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
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"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
bands = ormar.ManyToMany(Band, through=ArtistsBands)
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
artist: Optional[Artist] = ormar.ForeignKey(Artist, ondelete="CASCADE")
class Track(ormar.Model):
class Meta:
tablename = "tracks"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album, ondelete="CASCADE")
title: str = ormar.String(max_length=100)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.drop_all(engine)
metadata.create_all(engine)
yield
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(cleanup):
async with database:
artist = await Artist(name="Dr Alban").save()
await Album(name="Jamaica", artist=artist).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
@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

View File

@ -0,0 +1,55 @@
from typing import Optional
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
engine = sqlalchemy.create_engine(DATABASE_URL)
class Artist(ormar.Model):
class Meta:
tablename = "artists"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
artist: Optional[Artist] = ormar.ForeignKey(Artist, ondelete="CASCADE")
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
metadata.drop_all(engine)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
def test_simple_cascade():
inspector = sqlalchemy.inspect(engine)
columns = inspector.get_columns("albums")
assert len(columns) == 3
col_names = [col.get("name") for col in columns]
assert sorted(["id", "name", "artist"]) == sorted(col_names)
fks = inspector.get_foreign_keys("albums")
assert len(fks) == 1
assert fks[0]["name"] == "fk_albums_artists_id_artist"
assert fks[0]["constrained_columns"][0] == "artist"
assert fks[0]["referred_columns"][0] == "id"
assert fks[0]["options"].get("ondelete") == "CASCADE"

View File

@ -0,0 +1,429 @@
from typing import Optional
import databases
import pytest
import sqlalchemy
import ormar
from ormar.exceptions import NoMatch, MultipleMatches, RelationshipInstanceError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
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
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
title: str = ormar.String(max_length=100)
position: int = ormar.Integer()
play_count: int = ormar.Integer(nullable=True, default=0)
class Cover(ormar.Model):
class Meta:
tablename = "covers"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album, related_name="cover_pictures")
title: str = ormar.String(max_length=100)
class Organisation(ormar.Model):
class Meta:
tablename = "org"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
ident: str = ormar.String(max_length=100, choices=["ACME Ltd", "Other ltd"])
class Team(ormar.Model):
class Meta:
tablename = "teams"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
org: Optional[Organisation] = ormar.ForeignKey(Organisation)
name: str = ormar.String(max_length=100)
class Member(ormar.Model):
class Meta:
tablename = "members"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
team: Optional[Team] = ormar.ForeignKey(Team)
email: str = ormar.String(max_length=100)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.drop_all(engine)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
@pytest.mark.asyncio
async def test_wrong_query_foreign_key_type():
async with database:
with pytest.raises(RelationshipInstanceError):
Track(title="The Error", album="wrong_pk_type")
@pytest.mark.asyncio
async def test_setting_explicitly_empty_relation():
async with database:
track = Track(album=None, title="The Bird", position=1)
assert track.album is None
@pytest.mark.asyncio
async def test_related_name():
async with database:
async with database.transaction(force_rollback=True):
album = await Album.objects.create(name="Vanilla")
await Cover.objects.create(album=album, title="The cover file")
assert len(album.cover_pictures) == 1
@pytest.mark.asyncio
async def test_model_crud():
async with database:
async with database.transaction(force_rollback=True):
album = Album(name="Jamaica")
await album.save()
track1 = Track(album=album, title="The Bird", position=1)
track2 = Track(album=album, title="Heart don't stand a chance", position=2)
track3 = Track(album=album, title="The Waters", position=3)
await track1.save()
await track2.save()
await track3.save()
track = await Track.objects.get(title="The Bird")
assert track.album.pk == album.pk
assert isinstance(track.album, ormar.Model)
assert track.album.name is None
await track.album.load()
assert track.album.name == "Jamaica"
assert len(album.tracks) == 3
assert album.tracks[1].title == "Heart don't stand a chance"
album1 = await Album.objects.get(name="Jamaica")
assert album1.pk == album.pk
assert album1.tracks == []
await Track.objects.create(
album={"id": track.album.pk}, title="The Bird2", position=4
)
@pytest.mark.asyncio
async def test_select_related():
async with database:
async with database.transaction(force_rollback=True):
album = Album(name="Malibu")
await album.save()
track1 = Track(album=album, title="The Bird", position=1)
track2 = Track(album=album, title="Heart don't stand a chance", position=2)
track3 = Track(album=album, title="The Waters", position=3)
await track1.save()
await track2.save()
await track3.save()
fantasies = Album(name="Fantasies")
await fantasies.save()
track4 = Track(album=fantasies, title="Help I'm Alive", position=1)
track5 = Track(album=fantasies, title="Sick Muse", position=2)
track6 = Track(album=fantasies, title="Satellite Mind", position=3)
await track4.save()
await track5.save()
await track6.save()
track = await Track.objects.select_related("album").get(title="The Bird")
assert track.album.name == "Malibu"
tracks = await Track.objects.select_related("album").all()
assert len(tracks) == 6
@pytest.mark.asyncio
async def test_model_removal_from_relations():
async with database:
async with database.transaction(force_rollback=True):
album = Album(name="Chichi")
await album.save()
track1 = Track(album=album, title="The Birdman", position=1)
track2 = Track(album=album, title="Superman", position=2)
track3 = Track(album=album, title="Wonder Woman", position=3)
await track1.save()
await track2.save()
await track3.save()
assert len(album.tracks) == 3
await album.tracks.remove(track1)
assert len(album.tracks) == 2
assert track1.album is None
await track1.update()
track1 = await Track.objects.get(title="The Birdman")
assert track1.album is None
await album.tracks.add(track1)
assert len(album.tracks) == 3
assert track1.album == album
await track1.update()
track1 = await Track.objects.select_related("album__tracks").get(
title="The Birdman"
)
album = await Album.objects.select_related("tracks").get(name="Chichi")
assert track1.album == album
track1.remove(album, name="album")
assert track1.album is None
assert len(album.tracks) == 2
track2.remove(album, name="album")
assert track2.album is None
assert len(album.tracks) == 1
@pytest.mark.asyncio
async def test_fk_filter():
async with database:
async with database.transaction(force_rollback=True):
malibu = Album(name="Malibu%")
await malibu.save()
await Track.objects.create(album=malibu, title="The Bird", position=1)
await Track.objects.create(
album=malibu, title="Heart don't stand a chance", position=2
)
await Track.objects.create(album=malibu, title="The Waters", position=3)
fantasies = await Album.objects.create(name="Fantasies")
await Track.objects.create(
album=fantasies, title="Help I'm Alive", position=1
)
await Track.objects.create(album=fantasies, title="Sick Muse", position=2)
await Track.objects.create(
album=fantasies, title="Satellite Mind", position=3
)
tracks = (
await Track.objects.select_related("album")
.filter(album__name="Fantasies")
.all()
)
assert len(tracks) == 3
for track in tracks:
assert track.album.name == "Fantasies"
tracks = (
await Track.objects.select_related("album")
.filter(album__name__icontains="fan")
.all()
)
assert len(tracks) == 3
for track in tracks:
assert track.album.name == "Fantasies"
tracks = await Track.objects.filter(album__name__contains="Fan").all()
assert len(tracks) == 3
for track in tracks:
assert track.album.name == "Fantasies"
tracks = await Track.objects.filter(album__name__contains="Malibu%").all()
assert len(tracks) == 3
tracks = (
await Track.objects.filter(album=malibu).select_related("album").all()
)
assert len(tracks) == 3
for track in tracks:
assert track.album.name == "Malibu%"
tracks = await Track.objects.select_related("album").all(album=malibu)
assert len(tracks) == 3
for track in tracks:
assert track.album.name == "Malibu%"
@pytest.mark.asyncio
async def test_multiple_fk():
async with database:
async with database.transaction(force_rollback=True):
acme = await Organisation.objects.create(ident="ACME Ltd")
red_team = await Team.objects.create(org=acme, name="Red Team")
blue_team = await Team.objects.create(org=acme, name="Blue Team")
await Member.objects.create(team=red_team, email="a@example.org")
await Member.objects.create(team=red_team, email="b@example.org")
await Member.objects.create(team=blue_team, email="c@example.org")
await Member.objects.create(team=blue_team, email="d@example.org")
other = await Organisation.objects.create(ident="Other ltd")
team = await Team.objects.create(org=other, name="Green Team")
await Member.objects.create(team=team, email="e@example.org")
members = (
await Member.objects.select_related("team__org")
.filter(team__org__ident="ACME Ltd")
.all()
)
assert len(members) == 4
for member in members:
assert member.team.org.ident == "ACME Ltd"
@pytest.mark.asyncio
async def test_wrong_choices():
async with database:
async with database.transaction(force_rollback=True):
with pytest.raises(ValueError):
await Organisation.objects.create(ident="Test 1")
@pytest.mark.asyncio
async def test_pk_filter():
async with database:
async with database.transaction(force_rollback=True):
fantasies = await Album.objects.create(name="Test")
track = await Track.objects.create(
album=fantasies, title="Test1", position=1
)
await Track.objects.create(album=fantasies, title="Test2", position=2)
await Track.objects.create(album=fantasies, title="Test3", position=3)
tracks = (
await Track.objects.select_related("album").filter(pk=track.pk).all()
)
assert len(tracks) == 1
tracks = (
await Track.objects.select_related("album")
.filter(position=2, album__name="Test")
.all()
)
assert len(tracks) == 1
@pytest.mark.asyncio
async def test_limit_and_offset():
async with database:
async with database.transaction(force_rollback=True):
fantasies = await Album.objects.create(name="Limitless")
await Track.objects.create(
id=None, album=fantasies, title="Sample", position=1
)
await Track.objects.create(album=fantasies, title="Sample2", position=2)
await Track.objects.create(album=fantasies, title="Sample3", position=3)
tracks = await Track.objects.limit(1).all()
assert len(tracks) == 1
assert tracks[0].title == "Sample"
tracks = await Track.objects.limit(1).offset(1).all()
assert len(tracks) == 1
assert tracks[0].title == "Sample2"
@pytest.mark.asyncio
async def test_get_exceptions():
async with database:
async with database.transaction(force_rollback=True):
fantasies = await Album.objects.create(name="Test")
with pytest.raises(NoMatch):
await Album.objects.get(name="Test2")
await Track.objects.create(album=fantasies, title="Test1", position=1)
await Track.objects.create(album=fantasies, title="Test2", position=2)
await Track.objects.create(album=fantasies, title="Test3", position=3)
with pytest.raises(MultipleMatches):
await Track.objects.select_related("album").get(album=fantasies)
@pytest.mark.asyncio
async def test_wrong_model_passed_as_fk():
async with database:
async with database.transaction(force_rollback=True):
with pytest.raises(RelationshipInstanceError):
org = await Organisation.objects.create(ident="ACME Ltd")
await Track.objects.create(album=org, title="Test1", position=1)
@pytest.mark.asyncio
async def test_bulk_update_model_with_no_children():
async with database:
async with database.transaction(force_rollback=True):
album = await Album.objects.create(name="Test")
album.name = "Test2"
await Album.objects.bulk_update([album], columns=["name"])
updated_album = await Album.objects.get(id=album.id)
assert updated_album.name == "Test2"
@pytest.mark.asyncio
async def test_bulk_update_model_with_children():
async with database:
async with database.transaction(force_rollback=True):
best_seller = await Album.objects.create(name="to_be_best_seller")
best_seller2 = await Album.objects.create(name="to_be_best_seller2")
not_best_seller = await Album.objects.create(name="unpopular")
await Track.objects.create(
album=best_seller, title="t1", position=1, play_count=100
)
await Track.objects.create(
album=best_seller2, title="t2", position=1, play_count=100
)
await Track.objects.create(
album=not_best_seller, title="t3", position=1, play_count=3
)
await Track.objects.create(
album=best_seller, title="t4", position=1, play_count=500
)
tracks = (
await Track.objects.select_related("album")
.filter(play_count__gt=10)
.all()
)
best_seller_albums = {}
for track in tracks:
album = track.album
if album.id in best_seller_albums:
continue
album.is_best_seller = True
best_seller_albums[album.id] = album
await Album.objects.bulk_update(
best_seller_albums.values(), columns=["is_best_seller"]
)
best_seller_albums_db = await Album.objects.filter(
is_best_seller=True
).all()
assert len(best_seller_albums_db) == 2

View File

@ -0,0 +1,377 @@
from typing import Any, Sequence, cast
import databases
import pytest
import sqlalchemy
from pydantic.typing import ForwardRef
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class BaseMeta(ormar.ModelMeta):
database = database
metadata = metadata
class Category(ormar.Model):
class Meta(BaseMeta):
tablename = "categories"
id = ormar.Integer(primary_key=True)
name = ormar.String(max_length=40)
class PostCategory(ormar.Model):
class Meta(BaseMeta):
tablename = "posts_x_categories"
id: int = ormar.Integer(primary_key=True)
sort_order: int = ormar.Integer(nullable=True)
param_name: str = ormar.String(default="Name", max_length=200)
class Blog(ormar.Model):
class Meta(BaseMeta):
pass
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
class Post(ormar.Model):
class Meta(BaseMeta):
pass
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories = ormar.ManyToMany(Category, through=PostCategory)
blog = ormar.ForeignKey(Blog)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.drop_all(engine)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
class PostCategory2(ormar.Model):
class Meta(BaseMeta):
tablename = "posts_x_categories2"
id: int = ormar.Integer(primary_key=True)
sort_order: int = ormar.Integer(nullable=True)
class Post2(ormar.Model):
class Meta(BaseMeta):
pass
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories = ormar.ManyToMany(Category, through=ForwardRef("PostCategory2"))
@pytest.mark.asyncio
async def test_forward_ref_is_updated():
async with database:
assert Post2.Meta.requires_ref_update
Post2.update_forward_refs()
assert Post2.Meta.model_fields["postcategory2"].to == PostCategory2
@pytest.mark.asyncio
async def test_setting_fields_on_through_model():
async with database:
post = await Post(title="Test post").save()
category = await Category(name="Test category").save()
await post.categories.add(category)
assert hasattr(post.categories[0], "postcategory")
assert post.categories[0].postcategory is None
@pytest.mark.asyncio
async def test_setting_additional_fields_on_through_model_in_add():
async with database:
post = await Post(title="Test post").save()
category = await Category(name="Test category").save()
await post.categories.add(category, sort_order=1)
postcat = await PostCategory.objects.get()
assert postcat.sort_order == 1
@pytest.mark.asyncio
async def test_setting_additional_fields_on_through_model_in_create():
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category2", postcategory={"sort_order": 2}
)
postcat = await PostCategory.objects.get()
assert postcat.sort_order == 2
@pytest.mark.asyncio
async def test_getting_additional_fields_from_queryset() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1", postcategory={"sort_order": 1}
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 2}
)
await post.categories.all()
assert post.postcategory is None
assert post.categories[0].postcategory.sort_order == 1
assert post.categories[1].postcategory.sort_order == 2
post2 = await Post.objects.select_related("categories").get(
categories__name="Test category2"
)
assert post2.categories[0].postcategory.sort_order == 2
@pytest.mark.asyncio
async def test_only_one_side_has_through() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1", postcategory={"sort_order": 1}
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 2}
)
post2 = await Post.objects.select_related("categories").get()
assert post2.postcategory is None
assert post2.categories[0].postcategory is not None
await post2.categories.all()
assert post2.postcategory is None
assert post2.categories[0].postcategory is not None
categories = await Category.objects.select_related("posts").all()
assert isinstance(categories[0], Category)
assert categories[0].postcategory is None
assert categories[0].posts[0].postcategory is not None
@pytest.mark.asyncio
async def test_filtering_by_through_model() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 1, "param_name": "volume"},
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 2, "param_name": "area"}
)
post2 = (
await Post.objects.select_related("categories")
.filter(postcategory__sort_order__gt=1)
.get()
)
assert len(post2.categories) == 1
assert post2.categories[0].postcategory.sort_order == 2
post3 = await Post.objects.filter(
categories__postcategory__param_name="volume"
).get()
assert len(post3.categories) == 1
assert post3.categories[0].postcategory.param_name == "volume"
@pytest.mark.asyncio
async def test_deep_filtering_by_through_model() -> Any:
async with database:
blog = await Blog(title="My Blog").save()
post = await Post(title="Test post", blog=blog).save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 1, "param_name": "volume"},
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 2, "param_name": "area"}
)
blog2 = (
await Blog.objects.select_related("posts__categories")
.filter(posts__postcategory__sort_order__gt=1)
.get()
)
assert len(blog2.posts) == 1
assert len(blog2.posts[0].categories) == 1
assert blog2.posts[0].categories[0].postcategory.sort_order == 2
blog3 = await Blog.objects.filter(
posts__categories__postcategory__param_name="volume"
).get()
assert len(blog3.posts) == 1
assert len(blog3.posts[0].categories) == 1
assert blog3.posts[0].categories[0].postcategory.param_name == "volume"
@pytest.mark.asyncio
async def test_ordering_by_through_model() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 2, "param_name": "volume"},
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 1, "param_name": "area"}
)
await post.categories.create(
name="Test category3",
postcategory={"sort_order": 3, "param_name": "velocity"},
)
post2 = (
await Post.objects.select_related("categories")
.order_by("-postcategory__sort_order")
.get()
)
assert len(post2.categories) == 3
assert post2.categories[0].name == "Test category3"
assert post2.categories[2].name == "Test category2"
post3 = (
await Post.objects.select_related("categories")
.order_by("categories__postcategory__param_name")
.get()
)
assert len(post3.categories) == 3
assert post3.categories[0].postcategory.param_name == "area"
assert post3.categories[2].postcategory.param_name == "volume"
@pytest.mark.asyncio
async def test_update_through_models_from_queryset_on_through() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 2, "param_name": "volume"},
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 1, "param_name": "area"}
)
await post.categories.create(
name="Test category3",
postcategory={"sort_order": 3, "param_name": "velocity"},
)
await PostCategory.objects.filter(param_name="volume", post=post.id).update(
sort_order=4
)
post2 = (
await Post.objects.select_related("categories")
.order_by("-postcategory__sort_order")
.get()
)
assert len(post2.categories) == 3
assert post2.categories[0].postcategory.param_name == "volume"
assert post2.categories[2].postcategory.param_name == "area"
@pytest.mark.asyncio
async def test_update_through_model_after_load() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 2, "param_name": "volume"},
)
post2 = await Post.objects.select_related("categories").get()
assert len(post2.categories) == 1
await post2.categories[0].postcategory.load()
await post2.categories[0].postcategory.update(sort_order=3)
post3 = await Post.objects.select_related("categories").get()
assert len(post3.categories) == 1
assert post3.categories[0].postcategory.sort_order == 3
@pytest.mark.asyncio
async def test_update_through_from_related() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 2, "param_name": "volume"},
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 1, "param_name": "area"}
)
await post.categories.create(
name="Test category3",
postcategory={"sort_order": 3, "param_name": "velocity"},
)
await post.categories.filter(name="Test category3").update(
postcategory={"sort_order": 4}
)
post2 = (
await Post.objects.select_related("categories")
.order_by("postcategory__sort_order")
.get()
)
assert len(post2.categories) == 3
assert post2.categories[2].postcategory.sort_order == 4
@pytest.mark.asyncio
async def test_excluding_fields_on_through_model() -> Any:
async with database:
post = await Post(title="Test post").save()
await post.categories.create(
name="Test category1",
postcategory={"sort_order": 2, "param_name": "volume"},
)
await post.categories.create(
name="Test category2", postcategory={"sort_order": 1, "param_name": "area"}
)
await post.categories.create(
name="Test category3",
postcategory={"sort_order": 3, "param_name": "velocity"},
)
post2 = (
await Post.objects.select_related("categories")
.exclude_fields("postcategory__param_name")
.order_by("postcategory__sort_order")
.get()
)
assert len(post2.categories) == 3
assert post2.categories[0].postcategory.param_name is None
assert post2.categories[0].postcategory.sort_order == 1
assert post2.categories[2].postcategory.param_name is None
assert post2.categories[2].postcategory.sort_order == 3
post3 = (
await Post.objects.select_related("categories")
.fields({"postcategory": ..., "title": ...})
.exclude_fields({"postcategory": {"param_name", "sort_order"}})
.get()
)
assert len(post3.categories) == 3
for category in post3.categories:
assert category.postcategory.param_name is None
assert category.postcategory.sort_order is None

View File

@ -0,0 +1,240 @@
import asyncio
from typing import List, Optional
import databases
import pytest
import sqlalchemy
import ormar
from ormar.exceptions import ModelPersistenceError, NoMatch, RelationshipInstanceError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Author(ormar.Model):
class Meta:
tablename = "authors"
database = database
metadata = metadata
id: int = ormar.Integer(primary_key=True)
first_name: str = ormar.String(max_length=80)
last_name: str = ormar.String(max_length=80)
class Category(ormar.Model):
class Meta:
tablename = "categories"
database = database
metadata = metadata
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
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories: Optional[List[Category]] = ormar.ManyToMany(Category)
author: Optional[Author] = ormar.ForeignKey(Author)
@pytest.fixture(scope="module")
def event_loop():
loop = asyncio.get_event_loop()
yield loop
loop.close()
@pytest.fixture(autouse=True, scope="module")
async def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
@pytest.fixture(scope="function")
async def cleanup():
yield
async with database:
PostCategory = Post.Meta.model_fields["categories"].through
await PostCategory.objects.delete(each=True)
await Post.objects.delete(each=True)
await Category.objects.delete(each=True)
await Author.objects.delete(each=True)
@pytest.mark.asyncio
async def test_not_saved_raises_error(cleanup):
async with database:
guido = await Author(first_name="Guido", last_name="Van Rossum").save()
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = Category(name="News")
with pytest.raises(ModelPersistenceError):
await post.categories.add(news)
@pytest.mark.asyncio
async def test_not_existing_raises_error(cleanup):
async with database:
guido = await Author(first_name="Guido", last_name="Van Rossum").save()
post = await Post.objects.create(title="Hello, M2M", author=guido)
with pytest.raises(NoMatch):
await post.categories.get()
assert await post.categories.get_or_none() is None
@pytest.mark.asyncio
async def test_assigning_related_objects(cleanup):
async with database:
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = await Category.objects.create(name="News")
# Add a category to a post.
await post.categories.add(news)
# or from the other end:
await news.posts.add(post)
assert await post.categories.get_or_none(name="no exist") is None
assert await post.categories.get_or_none(name="News") == news
# Creating columns object from instance:
await post.categories.create(name="Tips")
assert len(post.categories) == 2
post_categories = await post.categories.all()
assert len(post_categories) == 2
@pytest.mark.asyncio
async def test_quering_of_the_m2m_models(cleanup):
async with database:
# orm can do this already.
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = await Category.objects.create(name="News")
# tl;dr: `post.categories` exposes the QuerySet API.
await post.categories.add(news)
post_categories = await post.categories.all()
assert len(post_categories) == 1
assert news == await post.categories.get(name="News")
num_posts = await news.posts.count()
assert num_posts == 1
posts_about_m2m = await news.posts.filter(title__contains="M2M").all()
assert len(posts_about_m2m) == 1
assert posts_about_m2m[0] == post
posts_about_python = await Post.objects.filter(categories__name="python").all()
assert len(posts_about_python) == 0
# Traversal of relationships: which categories has Guido contributed to?
category = await Category.objects.filter(posts__author=guido).get()
assert category == news
# or:
category2 = await Category.objects.filter(
posts__author__first_name="Guido"
).get()
assert category2 == news
@pytest.mark.asyncio
async def test_removal_of_the_relations(cleanup):
async with database:
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = await Category.objects.create(name="News")
await post.categories.add(news)
assert len(await post.categories.all()) == 1
await post.categories.remove(news)
assert len(await post.categories.all()) == 0
# or:
await news.posts.add(post)
assert len(await news.posts.all()) == 1
await news.posts.remove(post)
assert len(await news.posts.all()) == 0
# Remove all columns objects:
await post.categories.add(news)
await post.categories.clear()
assert len(await post.categories.all()) == 0
# post would also lose 'news' category when running:
await post.categories.add(news)
await news.delete()
assert len(await post.categories.all()) == 0
@pytest.mark.asyncio
async def test_selecting_related(cleanup):
async with database:
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = await Category.objects.create(name="News")
recent = await Category.objects.create(name="Recent")
await post.categories.add(news)
await post.categories.add(recent)
assert len(await post.categories.all()) == 2
# Loads categories and posts (2 queries) and perform the join in Python.
categories = await Category.objects.select_related("posts").all()
# No extra queries needed => no more `await`s required.
for category in categories:
assert category.posts[0] == post
news_posts = await news.posts.select_related("author").all()
assert news_posts[0].author == guido
assert (await post.categories.limit(1).all())[0] == news
assert (await post.categories.offset(1).limit(1).all())[0] == recent
assert await post.categories.first() == news
assert await post.categories.exists()
@pytest.mark.asyncio
async def test_selecting_related_fail_without_saving(cleanup):
async with database:
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = Post(title="Hello, M2M", author=guido)
with pytest.raises(RelationshipInstanceError):
await post.categories.all()
@pytest.mark.asyncio
async def test_adding_unsaved_related(cleanup):
async with database:
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = Category(name="News")
with pytest.raises(ModelPersistenceError):
await post.categories.add(news)
await news.save()
await post.categories.add(news)
assert len(await post.categories.all()) == 1
@pytest.mark.asyncio
async def test_removing_unsaved_related(cleanup):
async with database:
guido = await Author.objects.create(first_name="Guido", last_name="Van Rossum")
post = await Post.objects.create(title="Hello, M2M", author=guido)
news = Category(name="News")
with pytest.raises(NoMatch):
await post.categories.remove(news)

View File

@ -0,0 +1,389 @@
from typing import List, Optional
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class RandomSet(ormar.Model):
class Meta:
tablename = "randoms"
metadata = metadata
database = database
id: int = ormar.Integer(name="random_id", primary_key=True)
name: str = ormar.String(max_length=100)
class Tonation(ormar.Model):
class Meta:
tablename = "tonations"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(name="tonation_name", max_length=100)
rand_set: Optional[RandomSet] = ormar.ForeignKey(RandomSet)
class Division(ormar.Model):
class Meta:
tablename = "divisions"
metadata = metadata
database = database
id: int = ormar.Integer(name="division_id", primary_key=True)
name: str = ormar.String(max_length=100, nullable=True)
class Shop(ormar.Model):
class Meta:
tablename = "shops"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, nullable=True)
division: Optional[Division] = ormar.ForeignKey(Division)
class AlbumShops(ormar.Model):
class Meta:
tablename = "albums_x_shops"
metadata = metadata
database = database
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, nullable=True)
shops: List[Shop] = ormar.ManyToMany(to=Shop, through=AlbumShops)
class Track(ormar.Model):
class Meta:
tablename = "tracks"
metadata = metadata
database = database
id: int = ormar.Integer(name="track_id", primary_key=True)
album: Optional[Album] = ormar.ForeignKey(Album)
title: str = ormar.String(max_length=100)
position: int = ormar.Integer()
tonation: Optional[Tonation] = ormar.ForeignKey(Tonation, name="tonation_id")
class Cover(ormar.Model):
class Meta:
tablename = "covers"
metadata = metadata
database = database
id: int = ormar.Integer(primary_key=True)
album: Optional[Album] = ormar.ForeignKey(
Album, related_name="cover_pictures", name="album_id"
)
title: str = ormar.String(max_length=100)
artist: str = ormar.String(max_length=200, nullable=True)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.drop_all(engine)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
@pytest.mark.asyncio
async def test_prefetch_related():
async with database:
async with database.transaction(force_rollback=True):
album = Album(name="Malibu")
await album.save()
ton1 = await Tonation.objects.create(name="B-mol")
await Track.objects.create(
album=album, title="The Bird", position=1, tonation=ton1
)
await Track.objects.create(
album=album,
title="Heart don't stand a chance",
position=2,
tonation=ton1,
)
await Track.objects.create(
album=album, title="The Waters", position=3, tonation=ton1
)
await Cover.objects.create(title="Cover1", album=album, artist="Artist 1")
await Cover.objects.create(title="Cover2", album=album, artist="Artist 2")
fantasies = Album(name="Fantasies")
await fantasies.save()
await Track.objects.create(
album=fantasies, title="Help I'm Alive", position=1
)
await Track.objects.create(album=fantasies, title="Sick Muse", position=2)
await Track.objects.create(
album=fantasies, title="Satellite Mind", position=3
)
await Cover.objects.create(
title="Cover3", album=fantasies, artist="Artist 3"
)
await Cover.objects.create(
title="Cover4", album=fantasies, artist="Artist 4"
)
album = (
await Album.objects.filter(name="Malibu")
.prefetch_related(["tracks__tonation", "cover_pictures"])
.get()
)
assert len(album.tracks) == 3
assert album.tracks[0].title == "The Bird"
assert len(album.cover_pictures) == 2
assert album.cover_pictures[0].title == "Cover1"
assert (
album.tracks[0].tonation.name
== album.tracks[2].tonation.name
== "B-mol"
)
albums = await Album.objects.prefetch_related("tracks").all()
assert len(albums[0].tracks) == 3
assert len(albums[1].tracks) == 3
assert albums[0].tracks[0].title == "The Bird"
assert albums[1].tracks[0].title == "Help I'm Alive"
track = await Track.objects.prefetch_related(["album__cover_pictures"]).get(
title="The Bird"
)
assert track.album.name == "Malibu"
assert len(track.album.cover_pictures) == 2
assert track.album.cover_pictures[0].artist == "Artist 1"
track = (
await Track.objects.prefetch_related(["album__cover_pictures"])
.exclude_fields("album__cover_pictures__artist")
.get(title="The Bird")
)
assert track.album.name == "Malibu"
assert len(track.album.cover_pictures) == 2
assert track.album.cover_pictures[0].artist is None
tracks = await Track.objects.prefetch_related("album").all()
assert len(tracks) == 6
@pytest.mark.asyncio
async def test_prefetch_related_with_many_to_many():
async with database:
async with database.transaction(force_rollback=True):
div = await Division.objects.create(name="Div 1")
shop1 = await Shop.objects.create(name="Shop 1", division=div)
shop2 = await Shop.objects.create(name="Shop 2", division=div)
album = Album(name="Malibu")
await album.save()
await album.shops.add(shop1)
await album.shops.add(shop2)
await Track.objects.create(album=album, title="The Bird", position=1)
await Track.objects.create(
album=album, title="Heart don't stand a chance", position=2
)
await Track.objects.create(album=album, title="The Waters", position=3)
await Cover.objects.create(title="Cover1", album=album, artist="Artist 1")
await Cover.objects.create(title="Cover2", album=album, artist="Artist 2")
track = await Track.objects.prefetch_related(
["album__cover_pictures", "album__shops__division"]
).get(title="The Bird")
assert track.album.name == "Malibu"
assert len(track.album.cover_pictures) == 2
assert track.album.cover_pictures[0].artist == "Artist 1"
assert len(track.album.shops) == 2
assert track.album.shops[0].name == "Shop 1"
assert track.album.shops[0].division.name == "Div 1"
album2 = Album(name="Malibu 2")
await album2.save()
await album2.shops.add(shop1)
await album2.shops.add(shop2)
await Track.objects.create(album=album2, title="The Bird 2", position=1)
tracks = await Track.objects.prefetch_related(["album__shops"]).all()
assert tracks[0].album.name == "Malibu"
assert tracks[0].album.shops[0].name == "Shop 1"
assert tracks[3].album.name == "Malibu 2"
assert tracks[3].album.shops[0].name == "Shop 1"
assert tracks[0].album.shops[0] == tracks[3].album.shops[0]
assert id(tracks[0].album.shops[0]) == id(tracks[3].album.shops[0])
tracks[0].album.shops[0].name = "Dummy"
assert tracks[0].album.shops[0].name == tracks[3].album.shops[0].name
@pytest.mark.asyncio
async def test_prefetch_related_empty():
async with database:
async with database.transaction(force_rollback=True):
await Track.objects.create(title="The Bird", position=1)
track = await Track.objects.prefetch_related(["album__cover_pictures"]).get(
title="The Bird"
)
assert track.title == "The Bird"
assert track.album is None
@pytest.mark.asyncio
async def test_prefetch_related_with_select_related():
async with database:
async with database.transaction(force_rollback=True):
div = await Division.objects.create(name="Div 1")
shop1 = await Shop.objects.create(name="Shop 1", division=div)
shop2 = await Shop.objects.create(name="Shop 2", division=div)
album = Album(name="Malibu")
await album.save()
await album.shops.add(shop1)
await album.shops.add(shop2)
await Cover.objects.create(title="Cover1", album=album, artist="Artist 1")
await Cover.objects.create(title="Cover2", album=album, artist="Artist 2")
album = (
await Album.objects.select_related(["tracks", "shops"])
.filter(name="Malibu")
.prefetch_related(["cover_pictures", "shops__division"])
.first()
)
assert len(album.tracks) == 0
assert len(album.cover_pictures) == 2
assert album.shops[0].division.name == "Div 1"
rand_set = await RandomSet.objects.create(name="Rand 1")
ton1 = await Tonation.objects.create(name="B-mol", rand_set=rand_set)
await Track.objects.create(
album=album, title="The Bird", position=1, tonation=ton1
)
await Track.objects.create(
album=album,
title="Heart don't stand a chance",
position=2,
tonation=ton1,
)
await Track.objects.create(
album=album, title="The Waters", position=3, tonation=ton1
)
album = (
await Album.objects.select_related("tracks__tonation__rand_set")
.filter(name="Malibu")
.prefetch_related(["cover_pictures", "shops__division"])
.order_by(
["-shops__name", "-cover_pictures__artist", "shops__division__name"]
)
.get()
)
assert len(album.tracks) == 3
assert album.tracks[0].tonation == album.tracks[2].tonation == ton1
assert len(album.cover_pictures) == 2
assert album.cover_pictures[0].artist == "Artist 2"
assert len(album.shops) == 2
assert album.shops[0].name == "Shop 2"
assert album.shops[0].division.name == "Div 1"
track = (
await Track.objects.select_related("album")
.prefetch_related(["album__cover_pictures", "album__shops__division"])
.get(title="The Bird")
)
assert track.album.name == "Malibu"
assert len(track.album.cover_pictures) == 2
assert track.album.cover_pictures[0].artist == "Artist 1"
assert len(track.album.shops) == 2
assert track.album.shops[0].name == "Shop 1"
assert track.album.shops[0].division.name == "Div 1"
@pytest.mark.asyncio
async def test_prefetch_related_with_select_related_and_fields():
async with database:
async with database.transaction(force_rollback=True):
div = await Division.objects.create(name="Div 1")
shop1 = await Shop.objects.create(name="Shop 1", division=div)
shop2 = await Shop.objects.create(name="Shop 2", division=div)
album = Album(name="Malibu")
await album.save()
await album.shops.add(shop1)
await album.shops.add(shop2)
await Cover.objects.create(title="Cover1", album=album, artist="Artist 1")
await Cover.objects.create(title="Cover2", album=album, artist="Artist 2")
rand_set = await RandomSet.objects.create(name="Rand 1")
ton1 = await Tonation.objects.create(name="B-mol", rand_set=rand_set)
await Track.objects.create(
album=album, title="The Bird", position=1, tonation=ton1
)
await Track.objects.create(
album=album,
title="Heart don't stand a chance",
position=2,
tonation=ton1,
)
await Track.objects.create(
album=album, title="The Waters", position=3, tonation=ton1
)
album = (
await Album.objects.select_related("tracks__tonation__rand_set")
.filter(name="Malibu")
.prefetch_related(["cover_pictures", "shops__division"])
.exclude_fields({"shops": {"division": {"name"}}})
.get()
)
assert len(album.tracks) == 3
assert album.tracks[0].tonation == album.tracks[2].tonation == ton1
assert len(album.cover_pictures) == 2
assert album.cover_pictures[0].artist == "Artist 1"
assert len(album.shops) == 2
assert album.shops[0].name == "Shop 1"
assert album.shops[0].division.name is None
album = (
await Album.objects.select_related("tracks")
.filter(name="Malibu")
.prefetch_related(["cover_pictures", "shops__division"])
.fields(
{
"name": ...,
"shops": {"division"},
"cover_pictures": {"id": ..., "title": ...},
}
)
.exclude_fields({"shops": {"division": {"name"}}})
.get()
)
assert len(album.tracks) == 3
assert len(album.cover_pictures) == 2
assert album.cover_pictures[0].artist is None
assert album.cover_pictures[0].title is not None
assert len(album.shops) == 2
assert album.shops[0].name is None
assert album.shops[0].division is not None
assert album.shops[0].division.name is None

View File

@ -0,0 +1,100 @@
from typing import List, Optional
import databases
import sqlalchemy
from sqlalchemy import create_engine
import ormar
import pytest
from tests.settings import DATABASE_URL
db = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
class User(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "test_users"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=50)
class Signup(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "test_signup"
id: int = ormar.Integer(primary_key=True)
class Session(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "test_sessions"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=255, index=True)
some_text: str = ormar.Text()
some_other_text: Optional[str] = ormar.Text(nullable=True)
teacher: Optional[User] = ormar.ForeignKey(
User, nullable=True, related_name="teaching"
)
students: Optional[List[User]] = ormar.ManyToMany(
User, through=Signup, related_name="attending"
)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
@pytest.mark.asyncio
async def test_add_students():
async with db:
for user_id in [1, 2, 3, 4, 5]:
await User.objects.create(name=f"User {user_id}")
for name, some_text, some_other_text in [
("Session 1", "Some text 1", "Some other text 1"),
("Session 2", "Some text 2", "Some other text 2"),
("Session 3", "Some text 3", "Some other text 3"),
("Session 4", "Some text 4", "Some other text 4"),
("Session 5", "Some text 5", "Some other text 5"),
]:
await Session(
name=name, some_text=some_text, some_other_text=some_other_text
).save()
s1 = await Session.objects.get(pk=1)
s2 = await Session.objects.get(pk=2)
users = {}
for i in range(1, 6):
user = await User.objects.get(pk=i)
users[f"user_{i}"] = user
if i % 2 == 0:
await s1.students.add(user)
else:
await s2.students.add(user)
assert len(s1.students) > 0
assert len(s2.students) > 0
user = await User.objects.select_related("attending").get(pk=1)
assert user.attending is not None
assert len(user.attending) > 0
query = Session.objects.prefetch_related(["students", "teacher",])
sessions = await query.all()
assert len(sessions) == 5

View File

@ -0,0 +1,65 @@
# type: ignore
from typing import List, Optional
import databases
import pytest
import sqlalchemy
import ormar
from ormar.exceptions import ModelDefinitionError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Author(ormar.Model):
class Meta:
tablename = "authors"
database = database
metadata = metadata
id: int = ormar.Integer(primary_key=True)
first_name: str = ormar.String(max_length=80)
last_name: str = ormar.String(max_length=80)
class Category(ormar.Model):
class Meta:
tablename = "categories"
database = database
metadata = metadata
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=40)
def test_fk_error():
with pytest.raises(ModelDefinitionError):
class Post(ormar.Model):
class Meta:
tablename = "posts"
database = database
metadata = metadata
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories: Optional[List[Category]] = ormar.ManyToMany(Category)
author: Optional[Author] = ormar.ForeignKey(Author, default="aa")
def test_m2m_error():
with pytest.raises(ModelDefinitionError):
class Post(ormar.Model):
class Meta:
tablename = "posts"
database = database
metadata = metadata
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories: Optional[List[Category]] = ormar.ManyToMany(
Category, default="aa"
)

View File

@ -0,0 +1,158 @@
from typing import List, Optional
import databases
import sqlalchemy
from sqlalchemy import create_engine
import ormar
import pytest
from tests.settings import DATABASE_URL
db = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class Keyword(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "keywords"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=50)
class KeywordPrimaryModel(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "primary_models_keywords"
id: int = ormar.Integer(primary_key=True)
class PrimaryModel(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "primary_models"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=255, index=True)
some_text: str = ormar.Text()
some_other_text: Optional[str] = ormar.Text(nullable=True)
keywords: Optional[List[Keyword]] = ormar.ManyToMany(
Keyword, through=KeywordPrimaryModel
)
class SecondaryModel(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "secondary_models"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
primary_model: PrimaryModel = ormar.ForeignKey(
PrimaryModel, related_name="secondary_models",
)
@pytest.mark.asyncio
async def test_create_primary_models():
async with db:
for name, some_text, some_other_text in [
("Primary 1", "Some text 1", "Some other text 1"),
("Primary 2", "Some text 2", "Some other text 2"),
("Primary 3", "Some text 3", "Some other text 3"),
("Primary 4", "Some text 4", "Some other text 4"),
("Primary 5", "Some text 5", "Some other text 5"),
("Primary 6", "Some text 6", "Some other text 6"),
("Primary 7", "Some text 7", "Some other text 7"),
("Primary 8", "Some text 8", "Some other text 8"),
("Primary 9", "Some text 9", "Some other text 9"),
("Primary 10", "Some text 10", "Some other text 10"),
]:
await PrimaryModel(
name=name, some_text=some_text, some_other_text=some_other_text
).save()
for tag_id in [1, 2, 3, 4, 5]:
await Keyword.objects.create(name=f"Tag {tag_id}")
p1 = await PrimaryModel.objects.get(pk=1)
p2 = await PrimaryModel.objects.get(pk=2)
for i in range(1, 6):
keyword = await Keyword.objects.get(pk=i)
if i % 2 == 0:
await p1.keywords.add(keyword)
else:
await p2.keywords.add(keyword)
models = await PrimaryModel.objects.select_related("keywords").limit(5).all()
assert len(models) == 5
assert len(models[0].keywords) == 2
assert len(models[1].keywords) == 3
assert len(models[2].keywords) == 0
models2 = (
await PrimaryModel.objects.select_related("keywords")
.limit(5)
.offset(3)
.all()
)
assert len(models2) == 5
assert [x.name for x in models2] != [x.name for x in models]
assert [x.name for x in models2] == [
"Primary 4",
"Primary 5",
"Primary 6",
"Primary 7",
"Primary 8",
]
models3 = (
await PrimaryModel.objects.select_related("keywords")
.limit(5, limit_raw_sql=True)
.all()
)
assert len(models3) == 2
assert len(models3[0].keywords) == 2
assert len(models3[1].keywords) == 3
models4 = (
await PrimaryModel.objects.offset(1)
.select_related("keywords")
.limit(5, limit_raw_sql=True)
.all()
)
assert len(models4) == 3
assert [x.name for x in models4] == ["Primary 1", "Primary 2", "Primary 3"]
assert len(models4[0].keywords) == 1
assert len(models4[1].keywords) == 3
assert len(models4[2].keywords) == 0
models5 = (
await PrimaryModel.objects.select_related("keywords")
.offset(2, limit_raw_sql=True)
.limit(5)
.all()
)
assert len(models5) == 3
assert [x.name for x in models5] == ["Primary 2", "Primary 3", "Primary 4"]
assert len(models5[0].keywords) == 3
assert len(models5[1].keywords) == 0
assert len(models5[2].keywords) == 0
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)

View File

@ -0,0 +1,130 @@
# type: ignore
from datetime import date
from typing import List, Optional, Union
import databases
import pytest
import sqlalchemy
from sqlalchemy import create_engine
import ormar
from ormar import ModelDefinitionError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
class MainMeta(ormar.ModelMeta):
metadata = metadata
database = database
class Role(ormar.Model):
class Meta(MainMeta):
pass
name: str = ormar.String(primary_key=True, max_length=1000)
order: int = ormar.Integer(default=0, name="sort_order")
description: str = ormar.Text()
class Company(ormar.Model):
class Meta(MainMeta):
pass
name: str = ormar.String(primary_key=True, max_length=1000)
class UserRoleCompany(ormar.Model):
class Meta(MainMeta):
pass
class User(ormar.Model):
class Meta(MainMeta):
pass
registrationnumber: str = ormar.String(primary_key=True, max_length=1000)
company: Company = ormar.ForeignKey(Company)
company2: Company = ormar.ForeignKey(Company, related_name="secondary_users")
name: str = ormar.Text()
role: Optional[Role] = ormar.ForeignKey(Role)
roleforcompanies: Optional[Union[Company, List[Company]]] = ormar.ManyToMany(
Company, through=UserRoleCompany, related_name="role_users"
)
lastupdate: date = ormar.DateTime(server_default=sqlalchemy.func.now())
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
def test_wrong_model():
with pytest.raises(ModelDefinitionError):
class User(ormar.Model):
class Meta(MainMeta):
pass
registrationnumber: str = ormar.Text(primary_key=True)
company: Company = ormar.ForeignKey(Company)
company2: Company = ormar.ForeignKey(Company)
@pytest.mark.asyncio
async def test_create_primary_models():
async with database:
await Role.objects.create(
name="user", order=0, description="no administration right"
)
role_1 = await Role.objects.create(
name="admin", order=1, description="standard administration right"
)
await Role.objects.create(
name="super_admin", order=2, description="super administration right"
)
assert await Role.objects.count() == 3
company_0 = await Company.objects.create(name="Company")
company_1 = await Company.objects.create(name="Subsidiary Company 1")
company_2 = await Company.objects.create(name="Subsidiary Company 2")
company_3 = await Company.objects.create(name="Subsidiary Company 3")
assert await Company.objects.count() == 4
user = await User.objects.create(
registrationnumber="00-00000", company=company_0, name="admin", role=role_1
)
assert await User.objects.count() == 1
await user.delete()
assert await User.objects.count() == 0
user = await User.objects.create(
registrationnumber="00-00000",
company=company_0,
company2=company_3,
name="admin",
role=role_1,
)
await user.roleforcompanies.add(company_1)
await user.roleforcompanies.add(company_2)
users = await User.objects.select_related(
["company", "company2", "roleforcompanies"]
).all()
assert len(users) == 1
assert len(users[0].roleforcompanies) == 2
assert len(users[0].roleforcompanies[0].role_users) == 1
assert users[0].company.name == "Company"
assert len(users[0].company.users) == 1
assert users[0].company2.name == "Subsidiary Company 3"
assert len(users[0].company2.secondary_users) == 1
users = await User.objects.select_related("roleforcompanies").all()
assert len(users) == 1
assert len(users[0].roleforcompanies) == 2

View File

@ -0,0 +1,93 @@
from typing import List, Optional
import databases
import pytest
import sqlalchemy
from sqlalchemy import create_engine
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
class User(ormar.Model):
class Meta:
metadata = metadata
database = database
tablename = "test_users"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=50)
class Signup(ormar.Model):
class Meta:
metadata = metadata
database = database
tablename = "test_signup"
id: int = ormar.Integer(primary_key=True)
class Session(ormar.Model):
class Meta:
metadata = metadata
database = database
tablename = "test_sessions"
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=255, index=True)
some_text: str = ormar.Text()
some_other_text: Optional[str] = ormar.Text(nullable=True)
students: Optional[List[User]] = ormar.ManyToMany(User, through=Signup)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
@pytest.mark.asyncio
async def test_list_sessions_for_user():
async with database:
for user_id in [1, 2, 3, 4, 5]:
await User.objects.create(name=f"User {user_id}")
for name, some_text, some_other_text in [
("Session 1", "Some text 1", "Some other text 1"),
("Session 2", "Some text 2", "Some other text 2"),
("Session 3", "Some text 3", "Some other text 3"),
("Session 4", "Some text 4", "Some other text 4"),
("Session 5", "Some text 5", "Some other text 5"),
]:
await Session(
name=name, some_text=some_text, some_other_text=some_other_text
).save()
s1 = await Session.objects.get(pk=1)
s2 = await Session.objects.get(pk=2)
users = {}
for i in range(1, 6):
user = await User.objects.get(pk=i)
users[f"user_{i}"] = user
if i % 2 == 0:
await s1.students.add(user)
else:
await s2.students.add(user)
assert len(s1.students) == 2
assert len(s2.students) == 3
assert [x.pk for x in s1.students] == [2, 4]
assert [x.pk for x in s2.students] == [1, 3, 5]
user = await User.objects.select_related("sessions").get(pk=1)
assert user.sessions is not None
assert len(user.sessions) > 0

View File

@ -0,0 +1,51 @@
# type: ignore
import databases
import pytest
import sqlalchemy
import ormar
from ormar import ModelDefinitionError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
def test_through_with_relation_fails():
class BaseMeta(ormar.ModelMeta):
database = database
metadata = metadata
class Category(ormar.Model):
class Meta(BaseMeta):
tablename = "categories"
id = ormar.Integer(primary_key=True)
name = ormar.String(max_length=40)
class Blog(ormar.Model):
class Meta(BaseMeta):
pass
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
class PostCategory(ormar.Model):
class Meta(BaseMeta):
tablename = "posts_x_categories"
id: int = ormar.Integer(primary_key=True)
sort_order: int = ormar.Integer(nullable=True)
param_name: str = ormar.String(default="Name", max_length=200)
blog = ormar.ForeignKey(Blog)
with pytest.raises(ModelDefinitionError):
class Post(ormar.Model):
class Meta(BaseMeta):
pass
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories = ormar.ManyToMany(Category, through=PostCategory)