update docs, add load_all(), tests for load_all, make through field optional
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
from typing import Optional, Union, List
|
||||
from typing import List, Optional
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
@ -23,13 +23,6 @@ class Child(ormar.Model):
|
||||
born_year: int = ormar.Integer(name="year_born", nullable=True)
|
||||
|
||||
|
||||
class ArtistChildren(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "children_x_artists"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class Artist(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "artists"
|
||||
@ -40,9 +33,7 @@ class Artist(ormar.Model):
|
||||
first_name: str = ormar.String(name="fname", max_length=100)
|
||||
last_name: str = ormar.String(name="lname", max_length=100)
|
||||
born_year: int = ormar.Integer(name="year")
|
||||
children: Optional[Union[Child, List[Child]]] = ormar.ManyToMany(
|
||||
Child, through=ArtistChildren
|
||||
)
|
||||
children: Optional[List[Child]] = ormar.ManyToMany(Child)
|
||||
|
||||
|
||||
class Album(ormar.Model):
|
||||
|
||||
@ -42,18 +42,13 @@ class Category(ormar.Model):
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class ItemsXCategories(ormar.Model):
|
||||
class Meta(LocalMeta):
|
||||
tablename = "items_x_categories"
|
||||
|
||||
|
||||
class Item(ormar.Model):
|
||||
class Meta(LocalMeta):
|
||||
pass
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
categories = ormar.ManyToMany(Category, through=ItemsXCategories)
|
||||
categories = ormar.ManyToMany(Category)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
|
||||
@ -121,11 +121,11 @@ class Bus(Car):
|
||||
max_persons: int = ormar.Integer()
|
||||
|
||||
|
||||
class PersonsCar(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "cars_x_persons"
|
||||
metadata = metadata
|
||||
database = db
|
||||
# class PersonsCar(ormar.Model):
|
||||
# class Meta:
|
||||
# tablename = "cars_x_persons"
|
||||
# metadata = metadata
|
||||
# database = db
|
||||
|
||||
|
||||
class Car2(ormar.Model):
|
||||
@ -138,7 +138,9 @@ class Car2(ormar.Model):
|
||||
name: str = ormar.String(max_length=50)
|
||||
owner: Person = ormar.ForeignKey(Person, related_name="owned")
|
||||
co_owners: List[Person] = ormar.ManyToMany(
|
||||
Person, through=PersonsCar, related_name="coowned"
|
||||
Person,
|
||||
# through=PersonsCar,
|
||||
related_name="coowned",
|
||||
)
|
||||
created_date: datetime.datetime = ormar.DateTime(default=datetime.datetime.now)
|
||||
|
||||
|
||||
171
tests/test_load_all.py
Normal file
171
tests/test_load_all.py
Normal file
@ -0,0 +1,171 @@
|
||||
from typing import List
|
||||
|
||||
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 BaseMeta(ormar.ModelMeta):
|
||||
database = database
|
||||
metadata = metadata
|
||||
|
||||
|
||||
class Language(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "languages"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
level: str = ormar.String(max_length=150, default="Beginner")
|
||||
|
||||
|
||||
class CringeLevel(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "levels"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
language = ormar.ForeignKey(Language)
|
||||
|
||||
|
||||
class NickName(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "nicks"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
|
||||
is_lame: bool = ormar.Boolean(nullable=True)
|
||||
level: CringeLevel = ormar.ForeignKey(CringeLevel)
|
||||
|
||||
|
||||
class HQ(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "hqs"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
|
||||
nicks: List[NickName] = ormar.ManyToMany(NickName)
|
||||
|
||||
|
||||
class Company(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "companies"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100, nullable=False, name="company_name")
|
||||
founded: int = ormar.Integer(nullable=True)
|
||||
hq: HQ = ormar.ForeignKey(HQ, related_name="companies")
|
||||
|
||||
|
||||
@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_load_all_fk_rel():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
hq = await HQ.objects.create(name="Main")
|
||||
company = await Company.objects.create(name="Banzai", founded=1988, hq=hq)
|
||||
|
||||
hq = await HQ.objects.get(name="Main")
|
||||
await hq.load_all()
|
||||
|
||||
assert hq.companies[0] == company
|
||||
assert hq.companies[0].name == "Banzai"
|
||||
assert hq.companies[0].founded == 1988
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_all_many_to_many():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
nick1 = await NickName.objects.create(name="BazingaO", is_lame=False)
|
||||
nick2 = await NickName.objects.create(name="Bazinga20", is_lame=True)
|
||||
hq = await HQ.objects.create(name="Main")
|
||||
await hq.nicks.add(nick1)
|
||||
await hq.nicks.add(nick2)
|
||||
|
||||
hq = await HQ.objects.get(name="Main")
|
||||
await hq.load_all()
|
||||
|
||||
assert hq.nicks[0] == nick1
|
||||
assert hq.nicks[0].name == "BazingaO"
|
||||
|
||||
assert hq.nicks[1] == nick2
|
||||
assert hq.nicks[1].name == "Bazinga20"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_loading_reversed_relation():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
hq = await HQ.objects.create(name="Main")
|
||||
await Company.objects.create(name="Banzai", founded=1988, hq=hq)
|
||||
|
||||
company = await Company.objects.get(name="Banzai")
|
||||
await company.load_all()
|
||||
|
||||
assert company.hq == hq
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_loading_nested():
|
||||
async with database:
|
||||
async with database.transaction(force_rollback=True):
|
||||
language = await Language.objects.create(name="English")
|
||||
level = await CringeLevel.objects.create(name="High", language=language)
|
||||
level2 = await CringeLevel.objects.create(name="Low", language=language)
|
||||
nick1 = await NickName.objects.create(
|
||||
name="BazingaO", is_lame=False, level=level
|
||||
)
|
||||
nick2 = await NickName.objects.create(
|
||||
name="Bazinga20", is_lame=True, level=level2
|
||||
)
|
||||
hq = await HQ.objects.create(name="Main")
|
||||
await hq.nicks.add(nick1)
|
||||
await hq.nicks.add(nick2)
|
||||
|
||||
hq = await HQ.objects.get(name="Main")
|
||||
await hq.load_all(follow=True)
|
||||
|
||||
assert hq.nicks[0] == nick1
|
||||
assert hq.nicks[0].name == "BazingaO"
|
||||
assert hq.nicks[0].level.name == "High"
|
||||
assert hq.nicks[0].level.language.name == "English"
|
||||
|
||||
assert hq.nicks[1] == nick2
|
||||
assert hq.nicks[1].name == "Bazinga20"
|
||||
assert hq.nicks[1].level.name == "Low"
|
||||
assert hq.nicks[1].level.language.name == "English"
|
||||
|
||||
await hq.load_all(follow=True, exclude="nicks__level__language")
|
||||
assert len(hq.nicks) == 2
|
||||
assert hq.nicks[0].level.language is None
|
||||
assert hq.nicks[1].level.language is None
|
||||
|
||||
await hq.load_all(follow=True, exclude="nicks__level__language__level")
|
||||
assert len(hq.nicks) == 2
|
||||
assert hq.nicks[0].level.language is not None
|
||||
assert hq.nicks[0].level.language.level is None
|
||||
assert hq.nicks[1].level.language is not None
|
||||
assert hq.nicks[1].level.language.level is None
|
||||
|
||||
await hq.load_all(follow=True, exclude="nicks__level")
|
||||
assert len(hq.nicks) == 2
|
||||
assert hq.nicks[0].level is None
|
||||
assert hq.nicks[1].level is None
|
||||
|
||||
await hq.load_all(follow=True, exclude="nicks")
|
||||
assert len(hq.nicks) == 0
|
||||
@ -288,6 +288,25 @@ async def test_update_through_models_from_queryset_on_through() -> Any:
|
||||
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:
|
||||
@ -371,9 +390,10 @@ async def test_excluding_fields_on_through_model() -> Any:
|
||||
# ordering by in order_by (V)
|
||||
# updating in query (V)
|
||||
# updating from querysetproxy (V)
|
||||
# including/excluding in fields?
|
||||
# including/excluding in fields? (V)
|
||||
# make through optional? auto-generated for cases other fields are missing? (V)
|
||||
|
||||
# modifying from instance (both sides?) (X) <= no, the loaded one doesn't have relations
|
||||
# allowing to change fk fields names in through model? (X) <= separate issue
|
||||
|
||||
# allowing to change fk fields names in through model?
|
||||
# make through optional? auto-generated for cases other fields are missing?
|
||||
# prevent adding relation on through field definition
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import asyncio
|
||||
from typing import List, Union, Optional
|
||||
from typing import List, Optional
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
@ -34,13 +34,6 @@ class Category(ormar.Model):
|
||||
name: str = ormar.String(max_length=40)
|
||||
|
||||
|
||||
class PostCategory(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "posts_categories"
|
||||
database = database
|
||||
metadata = metadata
|
||||
|
||||
|
||||
class Post(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "posts"
|
||||
@ -49,9 +42,7 @@ class Post(ormar.Model):
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
title: str = ormar.String(max_length=200)
|
||||
categories: Optional[Union[Category, List[Category]]] = ormar.ManyToMany(
|
||||
Category, through=PostCategory
|
||||
)
|
||||
categories: Optional[List[Category]] = ormar.ManyToMany(Category)
|
||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||
|
||||
|
||||
@ -74,6 +65,7 @@ async def create_test_database():
|
||||
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)
|
||||
|
||||
@ -85,13 +85,6 @@ class Car(ormar.Model):
|
||||
factory: Optional[Factory] = ormar.ForeignKey(Factory)
|
||||
|
||||
|
||||
class UsersCar(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "cars_x_users"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class User(ormar.Model):
|
||||
class Meta:
|
||||
tablename = "users"
|
||||
@ -100,7 +93,7 @@ class User(ormar.Model):
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
cars: List[Car] = ormar.ManyToMany(Car, through=UsersCar)
|
||||
cars: List[Car] = ormar.ManyToMany(Car)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
|
||||
Reference in New Issue
Block a user