WIP - Pydantic v2 support (#1238)

* WIP

* WIP - make test_model_definition tests pass

* WIP - make test_model_methods pass

* WIP - make whole test suit at least run - failing 49/443 tests

* WIP fix part of the getting pydantic tests as types of fields are now kept in core schema and not on fieldsinfo

* WIP fix validation in update by creating individual fields validators, failing 36/443

* WIP fix __pydantic_extra__ in intializing model, fix test related to pydantic config checks, failing 32/442

* WIP - fix enum schema in model_json_schema, failing 31/442

* WIP - fix copying through model, fix setting pydantic fields on through, fix default config and inheriting from it, failing 26/442

* WIP fix tests checking pydantic schema, fix excluding parent fields, failing 21/442

* WIP some missed files

* WIP - fix validators inheritance and fix validators in generated pydantic, failing 17/442

* WIP - fix through models setting - only on reverse side of relation, but always on reverse side, failing 15/442

* WIP - fix through models setting - only on reverse side of relation, but always on reverse side, failing 15/442

* WIP - working on proper populating __dict__ for relations for new schema dumping, some work on openapi docs, failing 13/442

* WIP - remove property fields as pydantic has now computed_field on its own, failing 9/442

* WIP - fixes in docs, failing 8/442

* WIP - fix tests for largebinary schema, wrapped bytes fields fail in pydantic, will be fixed in pydantic-core, remaining is circural schema for related models, failing 6/442

* WIP - fix to pk only models in schemas

* Getting test suites to pass (#1249)

* wip, fixing tests

* iteration, fixing some more tests

* iteration, fixing some more tests

* adhere to comments

* adhere to comments

* remove unnecessary dict call, re-add getattribute for testing

* todo for reverse relationship

* adhere to comments, remove prints

* solve circular refs

* all tests pass 🎉

* remove 3.7 from tests

* add lint and type check jobs

* reforat with ruff, fix jobs

* rename jobs

* fix imports

* fix evaluate in py3.8

* partially fix coverage

* fix coverage, add more tests

* fix test ids

* fix test ids

* fix lint, fix docs, make docs fully working scripts, add test docs job

* fix pyproject

* pin py ver in test docs

* change dir in test docs

* fix pydantic warning hack

* rm poetry call in test_docs

* switch to pathlib in test docs

* remove coverage req test docs

* fix type check tests, fix part of types

* fix/skip next part of types

* fix next part of types

* fix next part of types

* fix coverage

* fix coverage

* fix type (bit dirty 🤷)

* fix some code smells

* change pre-commit

* tweak workflows

* remove no root from tests

* switch to full python path by passing sys.executable

* some small refactor in new base model, one sample test, change makefile

* small refactors to reduce complexity of methods

* temp add tests for prs against pydantic_v2

* remove all references to __fields__

* remove all references to construct, deprecate the method and update model_construct to be in line with pydantic

* deprecate dict and add model_dump, todo switch to model_dict in calls

* fix tests

* change to union

* change to union

* change to model_dump and model_dump_json from dict and json deprecated methods, deprecate them in ormar too

* finish switching dict() -> model_dump()

* finish switching json() -> model_dump_json()

* remove fully pydantic_only

* switch to extra for payment card, change missed json calls

* fix coverage - no more warnings internal

* fix coverage - no more warnings internal - part 2

* split model_construct into own and pydantic parts

* split determine pydantic field type

* change to new field validators

* fix benchmarks, add codspeed instead of pytest-benchmark, add action and gh workflow

* restore pytest-benchmark

* remove codspeed

* pin pydantic version, restore codspeed

* change on push to pydantic_v2 to trigger first one

* Use lifespan function instead of event (#1259)

* check return types

* fix imports order, set warnings=False on json that passes the dict, fix unnecessary loop in one of the test

* remove references to model's meta as it's now ormar config, rename related methods too

* filter out pydantic serializer warnings

* remove choices leftovers

* remove leftovers after property_fields, keep only enough to exclude them in initialization

* add migration guide

* fix meta references

* downgrade databases for now

* Change line numbers in documentation (#1265)

* proofread and fix the docs, part 1

* proofread and fix the docs for models

* proofread and fix the docs for fields

* proofread and fix the docs for relations

* proofread and fix rest of the docs, add release notes for 0.20

* create tables in new docs src

* cleanup old deps, uncomment docs publish on tag

* fix import reorder

---------

Co-authored-by: TouwaStar <30479449+TouwaStar@users.noreply.github.com>
Co-authored-by: Goran Mekić <meka@tilda.center>
This commit is contained in:
collerek
2024-03-23 19:28:28 +01:00
committed by GitHub
parent 3a206dd8dc
commit 500625f0ec
294 changed files with 8132 additions and 9311 deletions

View File

@ -1,10 +1,10 @@
import random
import databases
import ormar
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
@ -16,10 +16,11 @@ def key():
class Model(ormar.Model):
class Meta:
tablename = "models"
metadata = metadata
database = database
ormar_config = ormar.OrmarConfig(
tablename="models",
metadata=metadata,
database=database,
)
id: str = ormar.String(primary_key=True, default=key, max_length=8)
name: str = ormar.String(max_length=32)

View File

@ -2,13 +2,13 @@ from random import choice
from string import ascii_uppercase
import databases
import ormar
import pytest
import pytest_asyncio
import sqlalchemy
from ormar import Float, String
from sqlalchemy import create_engine
import ormar
from ormar import Float, String
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
@ -19,14 +19,14 @@ def get_id() -> str:
return "".join(choice(ascii_uppercase) for _ in range(12))
class MainMeta(ormar.ModelMeta):
metadata = metadata
database = database
base_ormar_config = ormar.OrmarConfig(
metadata=metadata,
database=database,
)
class PositionOrm(ormar.Model):
class Meta(MainMeta):
pass
ormar_config = base_ormar_config.copy()
name: str = String(primary_key=True, max_length=50)
x: float = Float()
@ -35,8 +35,7 @@ class PositionOrm(ormar.Model):
class PositionOrmDef(ormar.Model):
class Meta(MainMeta):
pass
ormar_config = base_ormar_config.copy()
name: str = String(primary_key=True, max_length=50, default=get_id)
x: float = Float()

View File

@ -1,11 +1,11 @@
import uuid
import databases
import ormar
import pytest
import sqlalchemy
from sqlalchemy import create_engine
import ormar
from tests.settings import DATABASE_URL
metadata = sqlalchemy.MetaData()
@ -13,10 +13,11 @@ db = databases.Database(DATABASE_URL)
class User(ormar.Model):
class Meta:
tablename = "user"
metadata = metadata
database = db
ormar_config = ormar.OrmarConfig(
tablename="user",
metadata=metadata,
database=db,
)
id: uuid.UUID = ormar.UUID(
primary_key=True, default=uuid.uuid4, uuid_format="string"
@ -29,10 +30,11 @@ class User(ormar.Model):
class Token(ormar.Model):
class Meta:
tablename = "token"
metadata = metadata
database = db
ormar_config = ormar.OrmarConfig(
tablename="token",
metadata=metadata,
database=db,
)
id = ormar.Integer(primary_key=True)
text = ormar.String(max_length=4, unique=True)

View File

@ -1,21 +1,16 @@
from typing import List, Optional
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
import pytest
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class Child(ormar.Model):
class Meta:
tablename = "children"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="children")
id: int = ormar.Integer(name="child_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
@ -24,10 +19,7 @@ class Child(ormar.Model):
class Artist(ormar.Model):
class Meta:
tablename = "artists"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="artists")
id: int = ormar.Integer(name="artist_id", primary_key=True)
first_name: str = ormar.String(name="fname", max_length=100)
@ -37,37 +29,28 @@ class Artist(ormar.Model):
class Album(ormar.Model):
class Meta:
tablename = "music_albums"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="music_albums")
id: int = ormar.Integer(name="album_id", primary_key=True)
name: str = ormar.String(name="album_name", max_length=100)
artist: Optional[Artist] = ormar.ForeignKey(Artist, name="artist_id")
@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)
create_test_database = init_tests(base_ormar_config)
def test_table_structure():
assert "album_id" in [x.name for x in Album.Meta.table.columns]
assert "album_name" in [x.name for x in Album.Meta.table.columns]
assert "fname" in [x.name for x in Artist.Meta.table.columns]
assert "lname" in [x.name for x in Artist.Meta.table.columns]
assert "year" in [x.name for x in Artist.Meta.table.columns]
assert "album_id" in [x.name for x in Album.ormar_config.table.columns]
assert "album_name" in [x.name for x in Album.ormar_config.table.columns]
assert "fname" in [x.name for x in Artist.ormar_config.table.columns]
assert "lname" in [x.name for x in Artist.ormar_config.table.columns]
assert "year" in [x.name for x in Artist.ormar_config.table.columns]
@pytest.mark.asyncio
async def test_working_with_aliases():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
artist = await Artist.objects.create(
first_name="Ted", last_name="Mosbey", born_year=1975
)
@ -124,7 +107,7 @@ async def test_working_with_aliases():
@pytest.mark.asyncio
async def test_bulk_operations_and_fields():
async with database:
async with base_ormar_config.database:
d1 = Child(first_name="Daughter", last_name="1", born_year=1990)
d2 = Child(first_name="Daughter", last_name="2", born_year=1991)
await Child.objects.bulk_create([d1, d2])
@ -155,8 +138,8 @@ async def test_bulk_operations_and_fields():
@pytest.mark.asyncio
async def test_working_with_aliases_get_or_create():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
artist, created = await Artist.objects.get_or_create(
first_name="Teddy", last_name="Bear", born_year=2020
)
@ -169,7 +152,7 @@ async def test_working_with_aliases_get_or_create():
assert artist == artist2
assert created is False
art3 = artist2.dict()
art3 = artist2.model_dump()
art3["born_year"] = 2019
await Artist.objects.update_or_create(**art3)

View File

@ -1,17 +1,15 @@
import datetime
from enum import Enum
import databases
import ormar
import pydantic
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()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config(force_rollback=True)
def time():
@ -24,10 +22,7 @@ class MyEnum(Enum):
class Example(ormar.Model):
class Meta:
tablename = "example"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="example")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=200, default="aaa")
@ -41,25 +36,17 @@ class Example(ormar.Model):
class EnumExample(ormar.Model):
class Meta:
tablename = "enum_example"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="enum_example")
id: int = ormar.Integer(primary_key=True)
size: MyEnum = ormar.Enum(enum_class=MyEnum, default=MyEnum.SMALL)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
def test_proper_enum_column_type():
assert Example.__fields__["size"].type_ == MyEnum
assert Example.model_fields["size"].__type__ == MyEnum
def test_accepts_only_proper_enums():
@ -73,7 +60,7 @@ def test_accepts_only_proper_enums():
@pytest.mark.asyncio
async def test_enum_bulk_operations():
async with database:
async with base_ormar_config.database:
examples = [EnumExample(), EnumExample()]
await EnumExample.objects.bulk_create(examples)
@ -90,7 +77,7 @@ async def test_enum_bulk_operations():
@pytest.mark.asyncio
async def test_enum_filter():
async with database:
async with base_ormar_config.database:
examples = [EnumExample(), EnumExample(size=MyEnum.BIG)]
await EnumExample.objects.bulk_create(examples)
@ -103,7 +90,7 @@ async def test_enum_filter():
@pytest.mark.asyncio
async def test_model_crud():
async with database:
async with base_ormar_config.database:
example = Example()
await example.save()
@ -130,15 +117,12 @@ async def test_model_crud():
@pytest.mark.asyncio
async def test_invalid_enum_field():
async with database:
async def test_invalid_enum_field() -> None:
async with base_ormar_config.database:
with pytest.raises(ModelDefinitionError):
class Example2(ormar.Model):
class Meta:
tablename = "example"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="example2")
id: int = ormar.Integer(primary_key=True)
size: MyEnum = ormar.Enum(enum_class=[])
size: MyEnum = ormar.Enum(enum_class=[]) # type: ignore

View File

@ -1,21 +1,14 @@
import uuid
from typing import ClassVar
import databases
import pytest
import sqlalchemy
from pydantic import root_validator
import ormar
from tests.settings import DATABASE_URL
import pytest
from pydantic import model_validator
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
class BaseMeta(ormar.ModelMeta):
database = database
metadata = metadata
base_ormar_config = create_config()
class Mol(ormar.Model):
@ -24,8 +17,7 @@ class Mol(ormar.Model):
"12345678-abcd-1234-abcd-123456789abc"
)
class Meta(BaseMeta):
tablename = "mols"
ormar_config = base_ormar_config.copy(tablename="mols")
id: uuid.UUID = ormar.UUID(primary_key=True, index=True, uuid_format="hex")
smiles: str = ormar.String(nullable=False, unique=True, max_length=256)
@ -36,7 +28,7 @@ class Mol(ormar.Model):
kwargs["id"] = self._UUID_NAMESPACE
super().__init__(**kwargs)
@root_validator()
@model_validator(mode="before")
def make_canonical_smiles_and_uuid(cls, values):
values["id"], values["smiles"] = cls.uuid(values["smiles"])
return values
@ -47,17 +39,12 @@ class Mol(ormar.Model):
return id_, smiles
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_json_column():
async with database:
async with base_ormar_config.database:
await Mol.objects.create(smiles="Cc1ccccc1")
count = await Mol.objects.count()
assert count == 1

View File

@ -1,21 +1,16 @@
from datetime import timezone, timedelta, datetime, date, time
import databases
import pytest
import sqlalchemy
from datetime import date, datetime, time, timedelta, timezone
import ormar
import pytest
from tests.settings import DATABASE_URL
from tests.lifespan import init_tests
from tests.settings import create_config
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
base_ormar_config = create_config()
class DateFieldsModel(ormar.Model):
class Meta:
database = database
metadata = metadata
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
created_date: datetime = ormar.DateTime(
@ -29,27 +24,21 @@ class DateFieldsModel(ormar.Model):
class SampleModel(ormar.Model):
class Meta:
database = database
metadata = metadata
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
updated_at: datetime = ormar.DateTime()
class TimeModel(ormar.Model):
class Meta:
database = database
metadata = metadata
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
elapsed: time = ormar.Time()
class DateModel(ormar.Model):
class Meta:
database = database
metadata = metadata
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
creation_date: date = ormar.Date()
@ -59,24 +48,15 @@ class MyModel(ormar.Model):
id: int = ormar.Integer(primary_key=True)
created_at: datetime = ormar.DateTime(timezone=True, nullable=False)
class Meta:
tablename = "mymodels"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="mymodels")
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_model_crud_with_timezone():
async with database:
async with base_ormar_config.database:
datemodel = await DateFieldsModel().save()
assert datemodel.created_date is not None
assert datemodel.updated_date is not None
@ -84,7 +64,7 @@ async def test_model_crud_with_timezone():
@pytest.mark.asyncio
async def test_query_with_datetime_in_filter():
async with database:
async with base_ormar_config.database:
creation_dt = datetime(2021, 5, 18, 0, 0, 0, 0)
sample = await SampleModel.objects.create(updated_at=creation_dt)
@ -98,7 +78,7 @@ async def test_query_with_datetime_in_filter():
@pytest.mark.asyncio
async def test_query_with_date_in_filter():
async with database:
async with base_ormar_config.database:
sample = await TimeModel.objects.create(elapsed=time(0, 20, 20))
await TimeModel.objects.create(elapsed=time(0, 12, 0))
await TimeModel.objects.create(elapsed=time(0, 19, 55))
@ -114,7 +94,7 @@ async def test_query_with_date_in_filter():
@pytest.mark.asyncio
async def test_query_with_time_in_filter():
async with database:
async with base_ormar_config.database:
await DateModel.objects.create(creation_date=date(2021, 5, 18))
sample2 = await DateModel.objects.create(creation_date=date(2021, 5, 19))
sample3 = await DateModel.objects.create(creation_date=date(2021, 5, 20))
@ -130,7 +110,7 @@ async def test_query_with_time_in_filter():
@pytest.mark.asyncio
async def test_filtering_by_timezone_with_timedelta():
async with database:
async with base_ormar_config.database:
now_utc = datetime.now(timezone.utc)
object = MyModel(created_at=now_utc)
await object.save()

View File

@ -1,38 +1,26 @@
# type: ignore
import databases
import pytest
import sqlalchemy
import ormar
from ormar import ModelDefinitionError, property_field
from tests.settings import DATABASE_URL
import pytest
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class Song(ormar.Model):
class Meta:
tablename = "songs"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="songs")
id: int = ormar.Integer(primary_key=True)
name: 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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_equality():
async with database:
async with base_ormar_config.database:
song1 = await Song.objects.create(name="Song")
song2 = await Song.objects.create(name="Song")
song3 = Song(name="Song")
@ -49,7 +37,7 @@ async def test_equality():
@pytest.mark.asyncio
async def test_hash_doesnt_change_with_fields_if_pk():
async with database:
async with base_ormar_config.database:
song1 = await Song.objects.create(name="Song")
prev_hash = hash(song1)
@ -59,7 +47,7 @@ async def test_hash_doesnt_change_with_fields_if_pk():
@pytest.mark.asyncio
async def test_hash_changes_with_fields_if_no_pk():
async with database:
async with base_ormar_config.database:
song1 = Song(name="Song")
prev_hash = hash(song1)

View File

@ -1,26 +1,26 @@
import databases
import sqlalchemy
import ormar
from ormar import Extra
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config(force_rollback=True)
class Child(ormar.Model):
class Meta(ormar.ModelMeta):
tablename = "children"
metadata = metadata
database = database
extra = Extra.ignore
ormar_config = base_ormar_config.copy(
tablename="children",
extra=Extra.ignore,
)
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)
create_test_database = init_tests(base_ormar_config)
def test_allow_extra_parameter():
child = Child(first_name="Test", last_name="Name", extra_param="Unexpected")
assert child.first_name == "Test"

View File

@ -1,57 +1,43 @@
import asyncio
from typing import Optional
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
import pytest
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config(force_rollback=True)
class SchoolClass(ormar.Model):
class Meta:
tablename = "app.schoolclasses"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="app.schoolclasses")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Category(ormar.Model):
class Meta:
tablename = "app.categories"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="app.categories")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Student(ormar.Model):
class Meta:
tablename = "app.students"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="app.students")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
gpa: float = ormar.Float()
schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass, related_name="students")
category: Optional[Category] = ormar.ForeignKey(Category, nullable=True, related_name="students")
schoolclass: Optional[SchoolClass] = ormar.ForeignKey(
SchoolClass, related_name="students"
)
category: Optional[Category] = ormar.ForeignKey(
Category, nullable=True, related_name="students"
)
@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)
create_test_database = init_tests(base_ormar_config)
async def create_data():
@ -59,27 +45,37 @@ async def create_data():
class2 = await SchoolClass.objects.create(name="Logic")
category = await Category.objects.create(name="Foreign")
category2 = await Category.objects.create(name="Domestic")
await Student.objects.create(name="Jane", category=category, schoolclass=class1, gpa=3.2)
await Student.objects.create(name="Judy", category=category2, schoolclass=class1, gpa=2.6)
await Student.objects.create(name="Jack", category=category2, schoolclass=class2, gpa=3.8)
await Student.objects.create(
name="Jane", category=category, schoolclass=class1, gpa=3.2
)
await Student.objects.create(
name="Judy", category=category2, schoolclass=class1, gpa=2.6
)
await Student.objects.create(
name="Jack", category=category2, schoolclass=class2, gpa=3.8
)
@pytest.mark.asyncio
async def test_quotes_left_join():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await create_data()
students = await Student.objects.filter(
(Student.schoolclass.name == "Math") | (Student.category.name == "Foreign")
(Student.schoolclass.name == "Math")
| (Student.category.name == "Foreign")
).all()
for student in students:
assert student.schoolclass.name == "Math" or student.category.name == "Foreign"
assert (
student.schoolclass.name == "Math"
or student.category.name == "Foreign"
)
@pytest.mark.asyncio
async def test_quotes_reverse_join():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await create_data()
schoolclasses = await SchoolClass.objects.filter(students__gpa__gt=3).all()
for schoolclass in schoolclasses:
@ -89,11 +85,12 @@ async def test_quotes_reverse_join():
@pytest.mark.asyncio
async def test_quotes_deep_join():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await create_data()
schoolclasses = await SchoolClass.objects.filter(students__category__name="Domestic").all()
schoolclasses = await SchoolClass.objects.filter(
students__category__name="Domestic"
).all()
for schoolclass in schoolclasses:
for student in schoolclass.students:
assert student.category.name == "Domestic"

View File

@ -1,31 +1,22 @@
import databases
import pytest
import sqlalchemy
import ormar
import pytest
from ormar import BaseField
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
class BaseMeta(ormar.ModelMeta):
metadata = metadata
database = database
base_ormar_config = create_config()
class PriceList(ormar.Model):
class Meta(BaseMeta):
tablename = "price_lists"
ormar_config = base_ormar_config.copy(tablename="price_lists")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
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)
@ -33,8 +24,7 @@ class Category(ormar.Model):
class Product(ormar.Model):
class Meta(BaseMeta):
tablename = "product"
ormar_config = base_ormar_config.copy(tablename="product")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
@ -42,19 +32,13 @@ class Product(ormar.Model):
category = ormar.ForeignKey(Category)
@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)
create_test_database = init_tests(base_ormar_config)
def test_fields_access():
# basic access
assert Product.id._field == Product.Meta.model_fields["id"]
assert Product.id.id == Product.Meta.model_fields["id"]
assert Product.id._field == Product.ormar_config.model_fields["id"]
assert Product.id.id == Product.ormar_config.model_fields["id"]
assert Product.pk.id == Product.id.id
assert isinstance(Product.id._field, BaseField)
assert Product.id._access_chain == "id"
@ -62,19 +46,19 @@ def test_fields_access():
# nested models
curr_field = Product.category.name
assert curr_field._field == Category.Meta.model_fields["name"]
assert curr_field._field == Category.ormar_config.model_fields["name"]
assert curr_field._access_chain == "category__name"
assert curr_field._source_model == Product
# deeper nesting
curr_field = Product.category.price_lists.name
assert curr_field._field == PriceList.Meta.model_fields["name"]
assert curr_field._field == PriceList.ormar_config.model_fields["name"]
assert curr_field._access_chain == "category__price_lists__name"
assert curr_field._source_model == Product
# reverse nesting
curr_field = PriceList.categories.products.rating
assert curr_field._field == Product.Meta.model_fields["rating"]
assert curr_field._field == Product.ormar_config.model_fields["rating"]
assert curr_field._access_chain == "categories__products__rating"
assert curr_field._source_model == PriceList
@ -191,8 +175,8 @@ def test_combining_groups_together():
@pytest.mark.asyncio
async def test_filtering_by_field_access():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
category = await Category(name="Toys").save()
product2 = await Product(
name="My Little Pony", rating=3.8, category=category

View File

@ -1,25 +1,17 @@
import uuid
from typing import List, Optional
import databases
import pytest
import sqlalchemy
from typing import Optional
import ormar
from tests.settings import DATABASE_URL
import pytest
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
class BaseMeta(ormar.ModelMeta):
metadata = metadata
database = database
base_ormar_config = create_config()
class PageLink(ormar.Model):
class Meta(BaseMeta):
tablename = "pagelinks"
ormar_config = base_ormar_config.copy(tablename="pagelinks")
id: int = ormar.Integer(primary_key=True)
value: str = ormar.String(max_length=2048)
@ -27,8 +19,7 @@ class PageLink(ormar.Model):
class Post(ormar.Model):
class Meta(BaseMeta):
tablename = "posts"
ormar_config = base_ormar_config.copy(tablename="posts")
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=500)
@ -38,16 +29,14 @@ class Post(ormar.Model):
class Department(ormar.Model):
class Meta(BaseMeta):
pass
ormar_config = base_ormar_config.copy()
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
name: str = ormar.String(max_length=100)
class Course(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)
@ -55,18 +44,13 @@ class Course(ormar.Model):
department: Optional[Department] = ormar.ForeignKey(Department)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_pass_int_values_as_fk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
link = await PageLink(id=1, value="test", country="USA").save()
await Post.objects.create(title="My post", link=link.id)
post_check = await Post.objects.select_related("link").get()
@ -75,7 +59,7 @@ async def test_pass_int_values_as_fk():
@pytest.mark.asyncio
async def test_pass_uuid_value_as_fk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
dept = await Department(name="Department test").save()
await Course(name="Test course", department=dept.id).save()

View File

@ -1,31 +1,24 @@
import uuid
import databases
import pytest
import sqlalchemy
import ormar
import pytest
from ormar.exceptions import QueryDefinitionError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class User(ormar.Model):
class Meta:
tablename = "users3"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="users3")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, default="")
class User2(ormar.Model):
class Meta:
tablename = "users4"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="users4")
id: uuid.UUID = ormar.UUID(
uuid_format="string", primary_key=True, default=uuid.uuid4
@ -34,10 +27,7 @@ class User2(ormar.Model):
class Task(ormar.Model):
class Meta:
tablename = "tasks"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="tasks")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, default="")
@ -45,10 +35,7 @@ class Task(ormar.Model):
class Task2(ormar.Model):
class Meta:
tablename = "tasks2"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="tasks2")
id: uuid.UUID = ormar.UUID(
uuid_format="string", primary_key=True, default=uuid.uuid4
@ -57,27 +44,21 @@ class Task2(ormar.Model):
user: User2 = ormar.ForeignKey(to=User2)
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_empty_result():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
async for user in User.objects.iterate():
pass # pragma: no cover
@pytest.mark.asyncio
async def test_model_iterator():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User.objects.create(name="Tom")
jane = await User.objects.create(name="Jane")
lucy = await User.objects.create(name="Lucy")
@ -88,8 +69,8 @@ async def test_model_iterator():
@pytest.mark.asyncio
async def test_model_iterator_filter():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User.objects.create(name="Tom")
await User.objects.create(name="Jane")
await User.objects.create(name="Lucy")
@ -100,8 +81,8 @@ async def test_model_iterator_filter():
@pytest.mark.asyncio
async def test_model_iterator_relations():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User.objects.create(name="Tom")
jane = await User.objects.create(name="Jane")
lucy = await User.objects.create(name="Lucy")
@ -120,8 +101,8 @@ async def test_model_iterator_relations():
@pytest.mark.asyncio
async def test_model_iterator_relations_queryset_proxy():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User.objects.create(name="Tom")
jane = await User.objects.create(name="Jane")
@ -146,8 +127,8 @@ async def test_model_iterator_relations_queryset_proxy():
@pytest.mark.asyncio
async def test_model_iterator_uneven_number_of_relations():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User.objects.create(name="Tom")
jane = await User.objects.create(name="Jane")
lucy = await User.objects.create(name="Lucy")
@ -168,8 +149,8 @@ async def test_model_iterator_uneven_number_of_relations():
@pytest.mark.asyncio
async def test_model_iterator_uuid_pk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User2.objects.create(name="Tom")
jane = await User2.objects.create(name="Jane")
lucy = await User2.objects.create(name="Lucy")
@ -180,8 +161,8 @@ async def test_model_iterator_uuid_pk():
@pytest.mark.asyncio
async def test_model_iterator_filter_uuid_pk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User2.objects.create(name="Tom")
await User2.objects.create(name="Jane")
await User2.objects.create(name="Lucy")
@ -192,8 +173,8 @@ async def test_model_iterator_filter_uuid_pk():
@pytest.mark.asyncio
async def test_model_iterator_relations_uuid_pk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User2.objects.create(name="Tom")
jane = await User2.objects.create(name="Jane")
lucy = await User2.objects.create(name="Lucy")
@ -212,8 +193,8 @@ async def test_model_iterator_relations_uuid_pk():
@pytest.mark.asyncio
async def test_model_iterator_relations_queryset_proxy_uuid_pk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User2.objects.create(name="Tom")
jane = await User2.objects.create(name="Jane")
@ -238,8 +219,8 @@ async def test_model_iterator_relations_queryset_proxy_uuid_pk():
@pytest.mark.asyncio
async def test_model_iterator_uneven_number_of_relations_uuid_pk():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User2.objects.create(name="Tom")
jane = await User2.objects.create(name="Jane")
lucy = await User2.objects.create(name="Lucy")
@ -262,7 +243,7 @@ async def test_model_iterator_uneven_number_of_relations_uuid_pk():
@pytest.mark.asyncio
async def test_model_iterator_with_prefetch_raises_error():
async with database:
async with base_ormar_config.database:
with pytest.raises(QueryDefinitionError):
async for user in User.objects.prefetch_related(User.tasks).iterate():
pass # pragma: no cover

View File

@ -1,38 +1,27 @@
from typing import List
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
import pytest
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class NickNames(ormar.Model):
class Meta:
tablename = "nicks"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="nicks")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
class NicksHq(ormar.Model):
class Meta:
tablename = "nicks_x_hq"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="nicks_x_hq")
class HQ(ormar.Model):
class Meta:
tablename = "hqs"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="hqs")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
@ -40,10 +29,7 @@ class HQ(ormar.Model):
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, nullable=False, name="company_name")
@ -51,47 +37,47 @@ class Company(ormar.Model):
hq: HQ = ormar.ForeignKey(HQ)
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_construct_with_empty_relation():
async with database:
async with database.transaction(force_rollback=True):
hq = await HQ.objects.create(name="Main")
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await HQ.objects.create(name="Main")
comp = Company(name="Banzai", hq=None, founded=1988)
comp2 = Company.construct(**dict(name="Banzai", hq=None, founded=1988))
assert comp.dict() == comp2.dict()
comp2 = Company.model_construct(
**dict(name="Banzai", hq=None, founded=1988)
)
assert comp.model_dump() == comp2.model_dump()
@pytest.mark.asyncio
async def test_init_and_construct_has_same_effect():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
hq = await HQ.objects.create(name="Main")
comp = Company(name="Banzai", hq=hq, founded=1988)
comp2 = Company.construct(**dict(name="Banzai", hq=hq, founded=1988))
assert comp.dict() == comp2.dict()
comp2 = Company.model_construct(**dict(name="Banzai", hq=hq, founded=1988))
assert comp.model_dump() == comp2.model_dump()
comp3 = Company.construct(**dict(name="Banzai", hq=hq.dict(), founded=1988))
assert comp.dict() == comp3.dict()
comp3 = Company.model_construct(
**dict(name="Banzai", hq=hq.model_dump(), founded=1988)
)
assert comp.model_dump() == comp3.model_dump()
@pytest.mark.asyncio
async def test_init_and_construct_has_same_effect_with_m2m():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
n1 = await NickNames(name="test").save()
n2 = await NickNames(name="test2").save()
hq = HQ(name="Main", nicks=[n1, n2])
hq2 = HQ.construct(**dict(name="Main", nicks=[n1, n2]))
assert hq.dict() == hq2.dict()
hq2 = HQ.model_construct(**dict(name="Main", nicks=[n1, n2]))
assert hq.model_dump() == hq2.model_dump()
hq3 = HQ.construct(**dict(name="Main", nicks=[n1.dict(), n2.dict()]))
assert hq.dict() == hq3.dict()
hq3 = HQ.model_construct(
**dict(name="Main", nicks=[n1.model_dump(), n2.model_dump()])
)
assert hq.model_dump() == hq3.model_dump()

View File

@ -1,30 +1,23 @@
# type: ignore
import asyncio
import datetime
import decimal
import databases
import pydantic
import pytest
import pytest_asyncio
import sqlalchemy
import typing
import ormar
import pydantic
import pytest
import sqlalchemy
from ormar.exceptions import ModelDefinitionError
from ormar.models import Model
from tests.settings import DATABASE_URL
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
database = databases.Database(DATABASE_URL, force_rollback=True)
base_ormar_config = create_config()
class ExampleModel(Model):
class Meta:
tablename = "example"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="example")
test: int = ormar.Integer(primary_key=True)
test_string: str = ormar.String(max_length=250)
@ -55,21 +48,13 @@ fields_to_check = [
class ExampleModel2(Model):
class Meta:
tablename = "examples"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="example2")
test: int = ormar.Integer(primary_key=True)
test_string: str = ormar.String(max_length=250)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
@pytest.fixture()
@ -84,7 +69,7 @@ def example():
def test_not_nullable_field_is_required():
with pytest.raises(pydantic.error_wrappers.ValidationError):
with pytest.raises(pydantic.ValidationError):
ExampleModel(test=1, test_string="test")
@ -116,9 +101,10 @@ def test_missing_metadata():
with pytest.raises(ModelDefinitionError):
class JsonSample2(ormar.Model):
class Meta:
tablename = "jsons2"
database = database
ormar_config = ormar.OrmarConfig(
tablename="jsons2",
database=base_ormar_config.database,
)
id: int = ormar.Integer(primary_key=True)
test_json = ormar.JSON(nullable=True)
@ -128,8 +114,18 @@ def test_missing_database():
with pytest.raises(ModelDefinitionError):
class JsonSample3(ormar.Model):
class Meta:
tablename = "jsons3"
ormar_config = ormar.OrmarConfig(tablename="jsons3")
id: int = ormar.Integer(primary_key=True)
test_json = ormar.JSON(nullable=True)
def test_wrong_pydantic_config():
with pytest.raises(ModelDefinitionError):
class ErrorSample(ormar.Model):
model_config = ["test"]
ormar_config = ormar.OrmarConfig(tablename="jsons3")
id: int = ormar.Integer(primary_key=True)
test_json = ormar.JSON(nullable=True)
@ -150,13 +146,15 @@ def test_primary_key_access_and_setting(example):
def test_pydantic_model_is_created(example):
assert issubclass(example.__class__, pydantic.BaseModel)
assert all([field in example.__fields__ for field in fields_to_check])
assert all([field in example.model_fields for field in fields_to_check])
assert example.test == 1
def test_sqlalchemy_table_is_created(example):
assert issubclass(example.Meta.table.__class__, sqlalchemy.Table)
assert all([field in example.Meta.table.columns for field in fields_to_check])
assert issubclass(example.ormar_config.table.__class__, sqlalchemy.Table)
assert all(
[field in example.ormar_config.table.columns for field in fields_to_check]
)
@typing.no_type_check
@ -164,10 +162,7 @@ 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
ormar_config = base_ormar_config.copy(tablename="example2")
test_string: str = ormar.String(max_length=250) # type: ignore
@ -178,37 +173,18 @@ def test_two_pks_in_model_definition():
@typing.no_type_check
class ExampleModel2(Model):
class Meta:
tablename = "example3"
database = database
metadata = metadata
ormar_config = base_ormar_config.copy(tablename="example3")
id: int = ormar.Integer(primary_key=True)
test_string: str = ormar.String(max_length=250, primary_key=True)
@typing.no_type_check
def test_setting_pk_column_as_pydantic_only_in_model_definition():
with pytest.raises(ModelDefinitionError):
class ExampleModel2(Model):
class Meta:
tablename = "example4"
database = database
metadata = metadata
test: int = ormar.Integer(primary_key=True, pydantic_only=True)
@typing.no_type_check
def test_decimal_error_in_model_definition():
with pytest.raises(ModelDefinitionError):
class ExampleModel2(Model):
class Meta:
tablename = "example5"
database = database
metadata = metadata
ormar_config = base_ormar_config.copy(tablename="example5")
test: decimal.Decimal = ormar.Decimal(primary_key=True)
@ -218,10 +194,7 @@ def test_binary_error_without_length_model_definition():
with pytest.raises(ModelDefinitionError):
class ExampleModel2(Model):
class Meta:
tablename = "example6"
database = database
metadata = metadata
ormar_config = base_ormar_config.copy(tablename="example6")
test: bytes = ormar.LargeBinary(primary_key=True, max_length=-1)
@ -231,10 +204,7 @@ def test_string_error_in_model_definition():
with pytest.raises(ModelDefinitionError):
class ExampleModel2(Model):
class Meta:
tablename = "example6"
database = database
metadata = metadata
ormar_config = base_ormar_config.copy(tablename="example6")
test: str = ormar.String(primary_key=True, max_length=0)

View File

@ -3,26 +3,22 @@ import base64
import datetime
import os
import uuid
from typing import List
from enum import Enum
import databases
import ormar
import pydantic
import pytest
import sqlalchemy
import ormar
from ormar.exceptions import ModelError, NoMatch, QueryDefinitionError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class JsonSample(ormar.Model):
class Meta:
tablename = "jsons"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="jsons")
id: int = ormar.Integer(primary_key=True)
test_json = ormar.JSON(nullable=True)
@ -33,13 +29,10 @@ blob2 = b"test2icac89uc98"
class LargeBinarySample(ormar.Model):
class Meta:
tablename = "my_bolbs"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="my_bolbs")
id: int = ormar.Integer(primary_key=True)
test_binary: bytes = ormar.LargeBinary(max_length=100000, choices=[blob, blob2])
test_binary: bytes = ormar.LargeBinary(max_length=100000)
blob3 = os.urandom(64)
@ -47,47 +40,34 @@ blob4 = os.urandom(100)
class LargeBinaryStr(ormar.Model):
class Meta:
tablename = "my_str_blobs"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="my_str_bolbs")
id: int = ormar.Integer(primary_key=True)
test_binary: str = ormar.LargeBinary(
max_length=100000, choices=[blob3, blob4], represent_as_base64_str=True
max_length=100000, represent_as_base64_str=True
)
class LargeBinaryNullableStr(ormar.Model):
class Meta:
tablename = "my_str_blobs2"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="my_str_bolbs2")
id: int = ormar.Integer(primary_key=True)
test_binary: str = ormar.LargeBinary(
max_length=100000,
choices=[blob3, blob4],
represent_as_base64_str=True,
nullable=True,
)
class UUIDSample(ormar.Model):
class Meta:
tablename = "uuids"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="uuids")
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4)
test_text: str = ormar.Text()
class UUIDSample2(ormar.Model):
class Meta:
tablename = "uuids2"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="uuids2")
id: uuid.UUID = ormar.UUID(
primary_key=True, default=uuid.uuid4, uuid_format="string"
@ -96,95 +76,78 @@ class UUIDSample2(ormar.Model):
class User(ormar.Model):
class Meta:
tablename = "users"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="users")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, default="")
class User2(ormar.Model):
class Meta:
tablename = "users2"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="users2")
id: str = ormar.String(primary_key=True, max_length=100)
name: str = ormar.String(max_length=100, default="")
class Product(ormar.Model):
class Meta:
tablename = "product"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="product")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
rating: int = ormar.Integer(minimum=1, maximum=5)
in_stock: bool = ormar.Boolean(default=False)
last_delivery: datetime.date = ormar.Date(default=datetime.datetime.now)
last_delivery: datetime.date = ormar.Date(default=datetime.date.today)
country_name_choices = ("Canada", "Algeria", "United States", "Belize")
country_taxed_choices = (True,)
country_country_code_choices = (-10, 1, 213, 1200)
class CountryNameEnum(Enum):
CANADA = "Canada"
ALGERIA = "Algeria"
USA = "United States"
BELIZE = "Belize"
class CountryCodeEnum(int, Enum):
MINUS_TEN = -10
ONE = 1
TWO_HUNDRED_THIRTEEN = 213
THOUSAND_TWO_HUNDRED = 1200
class Country(ormar.Model):
class Meta:
tablename = "country"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="country")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(
max_length=9, choices=country_name_choices, default="Canada"
)
taxed: bool = ormar.Boolean(choices=country_taxed_choices, default=True)
country_code: int = ormar.Integer(
minimum=0, maximum=1000, choices=country_country_code_choices, default=1
)
name: CountryNameEnum = ormar.Enum(enum_class=CountryNameEnum, default="Canada")
taxed: bool = ormar.Boolean(default=True)
country_code: int = ormar.Enum(enum_class=CountryCodeEnum, default=1)
class NullableCountry(ormar.Model):
class Meta:
tablename = "country2"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="country2")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=9, choices=country_name_choices, nullable=True)
name: CountryNameEnum = ormar.Enum(enum_class=CountryNameEnum, nullable=True)
class NotNullableCountry(ormar.Model):
class Meta:
tablename = "country3"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="country3")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=9, choices=country_name_choices, nullable=False)
name: CountryNameEnum = ormar.Enum(enum_class=CountryNameEnum, nullable=False)
@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)
create_test_database = init_tests(base_ormar_config)
def test_model_class():
assert list(User.Meta.model_fields.keys()) == ["id", "name"]
assert issubclass(User.Meta.model_fields["id"].__class__, pydantic.fields.FieldInfo)
assert User.Meta.model_fields["id"].primary_key is True
assert isinstance(User.Meta.model_fields["name"], pydantic.fields.FieldInfo)
assert User.Meta.model_fields["name"].max_length == 100
assert isinstance(User.Meta.table, sqlalchemy.Table)
assert list(User.ormar_config.model_fields.keys()) == ["id", "name"]
assert issubclass(
User.ormar_config.model_fields["id"].__class__, pydantic.fields.FieldInfo
)
assert User.ormar_config.model_fields["id"].primary_key is True
assert isinstance(User.ormar_config.model_fields["name"], pydantic.fields.FieldInfo)
assert User.ormar_config.model_fields["name"].max_length == 100
assert isinstance(User.ormar_config.table, sqlalchemy.Table)
def test_wrong_field_name():
@ -200,8 +163,8 @@ def test_model_pk():
@pytest.mark.asyncio
async def test_json_column():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await JsonSample.objects.create(test_json=dict(aa=12))
await JsonSample.objects.create(test_json='{"aa": 12}')
@ -216,8 +179,8 @@ async def test_json_column():
@pytest.mark.asyncio
async def test_binary_column():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await LargeBinarySample.objects.create(test_binary=blob)
await LargeBinarySample.objects.create(test_binary=blob2)
@ -232,8 +195,8 @@ async def test_binary_column():
@pytest.mark.asyncio
async def test_binary_str_column():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await LargeBinaryStr(test_binary=blob3).save()
await LargeBinaryStr.objects.create(test_binary=blob4)
@ -248,8 +211,8 @@ async def test_binary_str_column():
@pytest.mark.asyncio
async def test_binary_nullable_str_column():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await LargeBinaryNullableStr().save()
await LargeBinaryNullableStr.objects.create()
items = await LargeBinaryNullableStr.objects.all()
@ -279,8 +242,8 @@ async def test_binary_nullable_str_column():
@pytest.mark.asyncio
async def test_uuid_column():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
u1 = await UUIDSample.objects.create(test_text="aa")
u2 = await UUIDSample.objects.create(test_text="bb")
@ -303,7 +266,7 @@ async def test_uuid_column():
assert item2.id == item3.id
assert isinstance(item3.id, uuid.UUID)
u3 = await UUIDSample2(**u1.dict()).save()
u3 = await UUIDSample2(**u1.model_dump()).save()
u1_2 = await UUIDSample.objects.get(pk=u3.id)
assert u1_2 == u1
@ -314,8 +277,8 @@ async def test_uuid_column():
@pytest.mark.asyncio
async def test_model_crud():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
users = await User.objects.all()
assert users == []
@ -341,8 +304,8 @@ async def test_model_crud():
@pytest.mark.asyncio
async def test_model_get():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
with pytest.raises(ormar.NoMatch):
await User.objects.get()
@ -366,8 +329,8 @@ async def test_model_get():
@pytest.mark.asyncio
async def test_model_filter():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
await User.objects.create(name="Jane")
await User.objects.create(name="Lucy")
@ -422,7 +385,7 @@ async def test_model_filter():
@pytest.mark.asyncio
async def test_wrong_query_contains_model():
async with database:
async with base_ormar_config.database:
with pytest.raises(QueryDefinitionError):
product = Product(name="90%-Cotton", rating=2)
await Product.objects.filter(name__contains=product).count()
@ -430,8 +393,8 @@ async def test_wrong_query_contains_model():
@pytest.mark.asyncio
async def test_model_exists():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
assert await User.objects.filter(name="Tom").exists() is True
assert await User.objects.filter(name="Jane").exists() is False
@ -439,8 +402,8 @@ async def test_model_exists():
@pytest.mark.asyncio
async def test_model_count():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
await User.objects.create(name="Jane")
await User.objects.create(name="Lucy")
@ -451,8 +414,8 @@ async def test_model_count():
@pytest.mark.asyncio
async def test_model_limit():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
await User.objects.create(name="Jane")
await User.objects.create(name="Lucy")
@ -462,8 +425,8 @@ async def test_model_limit():
@pytest.mark.asyncio
async def test_model_limit_with_filter():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
await User.objects.create(name="Tom")
await User.objects.create(name="Tom")
@ -475,8 +438,8 @@ async def test_model_limit_with_filter():
@pytest.mark.asyncio
async def test_offset():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
await User.objects.create(name="Jane")
@ -486,8 +449,8 @@ async def test_offset():
@pytest.mark.asyncio
async def test_model_first():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
tom = await User.objects.create(name="Tom")
jane = await User.objects.create(name="Jane")
@ -500,27 +463,11 @@ async def test_model_first():
assert await User.objects.order_by("name").first() == jane
def not_contains(a, b):
return a not in b
def contains(a, b):
return a in b
def check_choices(values: tuple, ops: List):
ops_dict = {"in": contains, "out": not_contains}
checks = (country_name_choices, country_taxed_choices, country_country_code_choices)
assert all(
[ops_dict[op](value, check) for value, op, check in zip(values, ops, checks)]
)
@pytest.mark.asyncio
async def test_model_choices():
"""Test that choices work properly for various types of fields."""
async with database:
# Test valid choices.
"""Test that enum work properly for various types of fields."""
async with base_ormar_config.database:
# Test valid enums values.
await asyncio.gather(
Country.objects.create(name="Canada", taxed=True, country_code=1),
Country.objects.create(name="Algeria", taxed=True, country_code=213),
@ -529,54 +476,12 @@ async def test_model_choices():
with pytest.raises(ValueError):
name, taxed, country_code = "Saudi Arabia", True, 1
check_choices((name, taxed, country_code), ["out", "in", "in"])
await Country.objects.create(
name=name, taxed=taxed, country_code=country_code
)
with pytest.raises(ValueError):
name, taxed, country_code = "Algeria", False, 1
check_choices((name, taxed, country_code), ["in", "out", "in"])
await Country.objects.create(
name=name, taxed=taxed, country_code=country_code
)
with pytest.raises(ValueError):
name, taxed, country_code = "Algeria", True, 967
check_choices((name, taxed, country_code), ["in", "in", "out"])
await Country.objects.create(
name=name, taxed=taxed, country_code=country_code
)
with pytest.raises(ValueError):
name, taxed, country_code = (
"United States",
True,
1,
) # name is too long but is a valid choice
check_choices((name, taxed, country_code), ["in", "in", "in"])
await Country.objects.create(
name=name, taxed=taxed, country_code=country_code
)
with pytest.raises(ValueError):
name, taxed, country_code = (
"Algeria",
True,
-10,
) # country code is too small but is a valid choice
check_choices((name, taxed, country_code), ["in", "in", "in"])
await Country.objects.create(
name=name, taxed=taxed, country_code=country_code
)
with pytest.raises(ValueError):
name, taxed, country_code = (
"Algeria",
True,
1200,
) # country code is too large but is a valid choice
check_choices((name, taxed, country_code), ["in", "in", "in"])
await Country.objects.create(
name=name, taxed=taxed, country_code=country_code
)
@ -584,34 +489,24 @@ async def test_model_choices():
# test setting after init also triggers validation
with pytest.raises(ValueError):
name, taxed, country_code = "Algeria", True, 967
check_choices((name, taxed, country_code), ["in", "in", "out"])
country = Country()
country.country_code = country_code
with pytest.raises(ValueError):
name, taxed, country_code = "Saudi Arabia", True, 1
check_choices((name, taxed, country_code), ["out", "in", "in"])
country = Country()
country.name = name
with pytest.raises(ValueError):
name, taxed, country_code = "Algeria", False, 1
check_choices((name, taxed, country_code), ["in", "out", "in"])
country = Country()
country.taxed = taxed
# check also update from queryset
with pytest.raises(ValueError):
name, taxed, country_code = "Algeria", False, 1
check_choices((name, taxed, country_code), ["in", "out", "in"])
await Country(name="Belize").save()
await Country.objects.filter(name="Belize").update(name="Vietnam")
@pytest.mark.asyncio
async def test_nullable_field_model_choices():
"""Test that choices work properly for according to nullable setting"""
async with database:
async def test_nullable_field_model_enum():
"""Test that enum work properly for according to nullable setting"""
async with base_ormar_config.database:
c1 = await NullableCountry(name=None).save()
assert c1.name is None
@ -621,8 +516,8 @@ async def test_nullable_field_model_choices():
@pytest.mark.asyncio
async def test_start_and_end_filters():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Markos Uj")
await User.objects.create(name="Maqua Bigo")
await User.objects.create(name="maqo quidid")
@ -651,8 +546,8 @@ async def test_start_and_end_filters():
@pytest.mark.asyncio
async def test_get_and_first():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await User.objects.create(name="Tom")
await User.objects.create(name="Jane")
await User.objects.create(name="Lucy")
@ -681,4 +576,4 @@ async def test_get_and_first():
def test_constraints():
with pytest.raises(pydantic.ValidationError) as e:
Product(name="T-Shirt", rating=50, in_stock=True)
assert "ensure this value is less than or equal to 5" in str(e.value)
assert "Input should be less than or equal to 5 " in str(e.value)

View File

@ -1,22 +1,17 @@
import pickle
from typing import Optional
import databases
import pytest
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
import pytest
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class User(ormar.Model):
class Meta:
tablename = "users"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="users")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
@ -24,28 +19,19 @@ class User(ormar.Model):
class Post(ormar.Model):
class Meta:
tablename = "posts"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="posts")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
created_by: Optional[User] = ormar.ForeignKey(User)
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_dumping_and_loading_model_works():
async with database:
async with base_ormar_config.database:
user = await User(name="Test", properties={"aa": "bb"}).save()
post = Post(name="Test post")
await user.posts.add(post)

View File

@ -1,52 +1,44 @@
from typing import Dict, Optional
import databases
import ormar
import pytest
import sqlalchemy
from pydantic import Json, PositiveInt, ValidationError
import ormar
from tests.settings import DATABASE_URL
from tests.lifespan import init_tests
from tests.settings import create_config
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
base_ormar_config = create_config()
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: int = ormar.Integer(overwrite_pydantic_type=PositiveInt)
constraint_dict: Json = ormar.JSON(
overwrite_pydantic_type=Optional[Json[Dict[str, int]]] # type: ignore
)
overwrite_pydantic_type=Optional[Json[Dict[str, int]]]
) # type: ignore
@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)
create_test_database = init_tests(base_ormar_config)
def test_constraints():
with pytest.raises(ValidationError) as e:
OverwriteTest(my_int=-10)
assert "ensure this value is greater than 0" in str(e.value)
assert "Input should be greater than 0" in str(e.value)
with pytest.raises(ValidationError) as e:
OverwriteTest(my_int=10, constraint_dict={"aa": "ab"})
assert "value is not a valid integer" in str(e.value)
assert (
"Input should be a valid integer, unable to parse string as an integer"
in str(e.value)
)
@pytest.mark.asyncio
async def test_saving():
async with database:
async with base_ormar_config.database:
await OverwriteTest(my_int=5, constraint_dict={"aa": 123}).save()
test = await OverwriteTest.objects.get()

View File

@ -2,28 +2,19 @@ import sqlite3
from typing import Optional
import asyncpg
import databases
import pymysql
import sqlalchemy
from sqlalchemy import create_engine, text
import ormar
import pymysql
import pytest
from sqlalchemy import text
from tests.settings import DATABASE_URL
from tests.lifespan import init_tests
from tests.settings import create_config
db = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class BaseMeta(ormar.ModelMeta):
metadata = metadata
database = db
base_ormar_config = create_config()
class PrimaryModel(ormar.Model):
class Meta(BaseMeta):
tablename = "primary_models"
ormar_config = base_ormar_config.copy(tablename="primary_models")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=255, index=True)
@ -33,17 +24,12 @@ class PrimaryModel(ormar.Model):
)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_create_models():
async with db:
async with base_ormar_config.database:
primary = await PrimaryModel(
name="Foo", some_text="Bar", some_other_text="Baz"
).save()

View File

@ -1,39 +1,32 @@
import databases
import sqlalchemy
import ormar
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
class BaseMeta(ormar.ModelMeta):
metadata = metadata
database = database
base_ormar_config = create_config()
class AutoincrementModel(ormar.Model):
class Meta(BaseMeta):
pass
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True)
class NonAutoincrementModel(ormar.Model):
class Meta(BaseMeta):
pass
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True, autoincrement=False)
class ExplicitNullableModel(ormar.Model):
class Meta(BaseMeta):
pass
ormar_config = base_ormar_config.copy()
id: int = ormar.Integer(primary_key=True, nullable=True)
create_test_database = init_tests(base_ormar_config)
def test_pk_field_is_not_null():
for model in [AutoincrementModel, NonAutoincrementModel, ExplicitNullableModel]:
assert not model.Meta.table.c.get("id").nullable
assert not model.ormar_config.table.c.get("id").nullable

View File

@ -1,79 +1,68 @@
# type: ignore
import databases
import pytest
import sqlalchemy
import ormar
from ormar import ModelDefinitionError, property_field
from tests.settings import DATABASE_URL
import pytest
from pydantic import PydanticUserError, computed_field
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class Song(ormar.Model):
class Meta:
tablename = "songs"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="songs")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
sort_order: int = ormar.Integer()
@property_field
def sorted_name(self):
@computed_field
def sorted_name(self) -> str:
return f"{self.sort_order}: {self.name}"
@property_field
def sample(self):
@computed_field
def sample(self) -> str:
return "sample"
@property_field
def sample2(self):
@computed_field
def sample2(self) -> str:
return "sample2"
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_sort_order_on_main_model():
async with database:
async with base_ormar_config.database:
await Song.objects.create(name="Song 3", sort_order=3)
await Song.objects.create(name="Song 1", sort_order=1)
await Song.objects.create(name="Song 2", sort_order=2)
songs = await Song.objects.all()
song_dict = [song.dict() for song in songs]
song_dict = [song.model_dump() for song in songs]
assert all("sorted_name" in x for x in song_dict)
assert all(
x["sorted_name"] == f"{x['sort_order']}: {x['name']}" for x in song_dict
)
song_json = [song.json() for song in songs]
song_json = [song.model_dump_json() for song in songs]
assert all("sorted_name" in x for x in song_json)
check_include = songs[0].dict(include={"sample"})
check_include = songs[0].model_dump(include={"sample"})
assert "sample" in check_include
assert "sample2" not in check_include
assert "sorted_name" not in check_include
check_include = songs[0].dict(exclude={"sample"})
check_include = songs[0].model_dump(exclude={"sample"})
assert "sample" not in check_include
assert "sample2" in check_include
assert "sorted_name" in check_include
def test_wrong_definition():
with pytest.raises(ModelDefinitionError):
with pytest.raises(PydanticUserError):
class WrongModel(ormar.Model): # pragma: no cover
@property_field
@computed_field
def test(self, aa=10, bb=30):
pass

View File

@ -1,31 +1,24 @@
import random
from typing import Optional
import databases
import pytest
import sqlalchemy
from pydantic import BaseModel, Field, HttpUrl, PaymentCardNumber
import ormar
from tests.settings import DATABASE_URL
import pytest
from pydantic import BaseModel, Field, HttpUrl
from pydantic_extra_types.payment import PaymentCardNumber
database = databases.Database(DATABASE_URL)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
class BaseMeta(ormar.ModelMeta):
metadata = metadata
database = database
base_ormar_config = create_config()
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)
url: HttpUrl = "https://www.example.com" # type: ignore
number: Optional[PaymentCardNumber]
number: Optional[PaymentCardNumber] = None
CARD_NUMBERS = [
@ -42,8 +35,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)
@ -57,8 +49,7 @@ class PydanticTest(BaseModel):
class ModelTest3(ormar.Model):
class Meta(BaseMeta):
pass
ormar_config = base_ormar_config.copy()
def __init__(self, **kwargs):
kwargs["number"] = get_number()
@ -72,18 +63,12 @@ class ModelTest3(ormar.Model):
pydantic_test: PydanticTest
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_working_with_pydantic_fields():
async with database:
async with base_ormar_config.database:
test = ModelTest(name="Test")
assert test.name == "Test"
assert test.url == "https://www.example.com"
@ -103,7 +88,7 @@ async def test_working_with_pydantic_fields():
@pytest.mark.asyncio
async def test_default_factory_for_pydantic_fields():
async with database:
async with base_ormar_config.database:
test = ModelTest2(name="Test2", number="4000000000000002")
assert test.name == "Test2"
assert test.url == "https://www.example2.com"
@ -123,7 +108,7 @@ async def test_default_factory_for_pydantic_fields():
@pytest.mark.asyncio
async def test_init_setting_for_pydantic_fields():
async with database:
async with base_ormar_config.database:
test = ModelTest3(name="Test3")
assert test.name == "Test3"
assert test.url == "https://www.example3.com"

View File

@ -1,32 +1,28 @@
import datetime
import databases
import pytest
import sqlalchemy
import ormar
from ormar import property_field
from tests.settings import DATABASE_URL
import pydantic
import pytest
from pydantic import computed_field
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class Album(ormar.Model):
class Meta:
tablename = "albums"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="albums")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
timestamp: datetime.datetime = ormar.DateTime(pydantic_only=True)
timestamp: datetime.datetime = pydantic.Field(default=None)
@property_field
@computed_field
def name10(self) -> str:
return self.name + "_10"
@property_field
@computed_field
def name20(self) -> str:
return self.name + "_20"
@ -34,24 +30,18 @@ class Album(ormar.Model):
def name30(self) -> str:
return self.name + "_30"
@property_field
@computed_field
def name40(self) -> str:
return self.name + "_40"
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_pydantic_only_fields():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
album = await Album.objects.create(name="Hitchcock")
assert album.pk is not None
assert album.saved
@ -63,14 +53,14 @@ async def test_pydantic_only_fields():
album = await Album.objects.fields({"name", "timestamp"}).get()
assert album.timestamp is None
test_dict = album.dict()
test_dict = album.model_dump()
assert "timestamp" in test_dict
assert test_dict["timestamp"] is None
assert album.name30 == "Hitchcock_30"
album.timestamp = datetime.datetime.now()
test_dict = album.dict()
test_dict = album.model_dump()
assert "timestamp" in test_dict
assert test_dict["timestamp"] is not None
assert test_dict.get("name10") == "Hitchcock_10"

View File

@ -1,24 +1,16 @@
from typing import List
import databases
import sqlalchemy
import ormar
from pydantic import PrivateAttr
import ormar
from tests.settings import DATABASE_URL
from tests.lifespan import init_tests
from tests.settings import create_config
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
class BaseMeta(ormar.ModelMeta):
metadata = metadata
database = database
base_ormar_config = create_config()
class Subscription(ormar.Model):
class Meta(BaseMeta):
tablename = "subscriptions"
ormar_config = base_ormar_config.copy(tablename="subscriptions")
id: int = ormar.Integer(primary_key=True)
stripe_subscription_id: str = ormar.String(nullable=False, max_length=256)
@ -29,6 +21,9 @@ class Subscription(ormar.Model):
self._add_payments.append(payment)
create_test_database = init_tests(base_ormar_config)
def test_private_attribute():
sub = Subscription(stripe_subscription_id="2312312sad231")
sub.add_payment("test")

View File

@ -1,22 +1,17 @@
from typing import List
import databases
import pytest
import sqlalchemy
import ormar
import pytest
from ormar.exceptions import ModelPersistenceError
from tests.settings import DATABASE_URL
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
base_ormar_config = create_config()
class NickNames(ormar.Model):
class Meta:
tablename = "nicks"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="nicks")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
@ -24,17 +19,11 @@ class NickNames(ormar.Model):
class NicksHq(ormar.Model):
class Meta:
tablename = "nicks_x_hq"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="nicks_x_hq")
class HQ(ormar.Model):
class Meta:
tablename = "hqs"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="hqs")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100, nullable=False, name="hq_name")
@ -42,10 +31,7 @@ class HQ(ormar.Model):
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, nullable=False, name="company_name")
@ -53,19 +39,13 @@ class Company(ormar.Model):
hq: HQ = ormar.ForeignKey(HQ)
@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)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_instantiation_false_save_true():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
comp = Company(name="Banzai", founded=1988)
assert not comp.saved
await comp.save()
@ -74,8 +54,8 @@ async def test_instantiation_false_save_true():
@pytest.mark.asyncio
async def test_saved_edited_not_saved():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
comp = await Company.objects.create(name="Banzai", founded=1988)
assert comp.saved
comp.name = "Banzai2"
@ -96,8 +76,8 @@ async def test_saved_edited_not_saved():
@pytest.mark.asyncio
async def test_adding_related_gets_dirty():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
hq = await HQ.objects.create(name="Main")
comp = await Company.objects.create(name="Banzai", founded=1988)
assert comp.saved
@ -123,8 +103,8 @@ async def test_adding_related_gets_dirty():
@pytest.mark.asyncio
async def test_adding_many_to_many_does_not_gets_dirty():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
nick1 = await NickNames.objects.create(name="Bazinga", is_lame=False)
nick2 = await NickNames.objects.create(name="Bazinga2", is_lame=True)
@ -152,8 +132,8 @@ async def test_adding_many_to_many_does_not_gets_dirty():
@pytest.mark.asyncio
async def test_delete():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
comp = await Company.objects.create(name="Banzai", founded=1988)
assert comp.saved
await comp.delete()
@ -165,8 +145,8 @@ async def test_delete():
@pytest.mark.asyncio
async def test_load():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
comp = await Company.objects.create(name="Banzai", founded=1988)
assert comp.saved
comp.name = "AA"
@ -179,8 +159,8 @@ async def test_load():
@pytest.mark.asyncio
async def test_queryset_methods():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
await Company.objects.create(name="Banzai", founded=1988)
await Company.objects.create(name="Yuhu", founded=1989)
await Company.objects.create(name="Konono", founded=1990)
@ -208,7 +188,7 @@ async def test_queryset_methods():
assert comp3.pk == comp.pk
assert created is False
update_dict = comp.dict()
update_dict = comp.model_dump()
update_dict["founded"] = 2010
comp = await Company.objects.update_or_create(**update_dict)
assert comp.saved
@ -222,8 +202,8 @@ async def test_queryset_methods():
@pytest.mark.asyncio
async def test_bulk_methods():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
c1 = Company(name="Banzai", founded=1988)
c2 = Company(name="Yuhu", founded=1989)

View File

@ -1,23 +1,16 @@
from typing import Optional
import databases
import sqlalchemy
from sqlalchemy import create_engine
import ormar
import pytest
from tests.settings import DATABASE_URL
from tests.lifespan import init_tests
from tests.settings import create_config
db = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
base_ormar_config = create_config()
class PrimaryModel(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "primary_models"
ormar_config = base_ormar_config.copy(tablename="primary_models")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=255, index=True)
@ -27,10 +20,7 @@ class PrimaryModel(ormar.Model):
class SecondaryModel(ormar.Model):
class Meta:
metadata = metadata
database = db
tablename = "secondary_models"
ormar_config = base_ormar_config.copy(tablename="secondary_models")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
@ -39,18 +29,13 @@ class SecondaryModel(ormar.Model):
)
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_create_models():
async with db:
async with db.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
primary = await PrimaryModel(
name="Foo", some_text="Bar", some_other_text="Baz"
).save()

View File

@ -1,24 +1,18 @@
import asyncio
import time
from datetime import datetime
import databases
import ormar
import pytest
import sqlalchemy
from sqlalchemy import func, text
import ormar
from tests.settings import DATABASE_URL
from tests.lifespan import init_tests
from tests.settings import create_config
database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()
base_ormar_config = create_config()
class Product(ormar.Model):
class Meta:
tablename = "product"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="product")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
@ -27,25 +21,21 @@ class Product(ormar.Model):
created: datetime = ormar.DateTime(server_default=func.now())
@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)
create_test_database = init_tests(base_ormar_config)
def test_table_defined_properly():
assert Product.Meta.model_fields["created"].nullable
assert not Product.__fields__["created"].required
assert Product.Meta.table.columns["created"].server_default.arg.name == "now"
assert Product.ormar_config.model_fields["created"].nullable
assert not Product.model_fields["created"].is_required()
assert (
Product.ormar_config.table.columns["created"].server_default.arg.name == "now"
)
@pytest.mark.asyncio
async def test_model_creation():
async with database:
async with database.transaction(force_rollback=True):
async with base_ormar_config.database:
async with base_ormar_config.database.transaction(force_rollback=True):
p1 = Product(name="Test")
assert p1.created is None
await p1.save()

View File

@ -1,36 +1,25 @@
import databases
import pytest
import sqlalchemy
import ormar
import pytest
from ormar.models import Model
from tests.settings import DATABASE_URL
metadata = sqlalchemy.MetaData()
from tests.lifespan import init_tests
from tests.settings import create_config
database = databases.Database(DATABASE_URL, force_rollback=True)
base_ormar_config = create_config()
class Comment(Model):
class Meta(ormar.ModelMeta):
tablename = "comments"
metadata = metadata
database = database
ormar_config = base_ormar_config.copy(tablename="comments")
test: int = ormar.Integer(primary_key=True, comment="primary key of comments")
test_string: str = ormar.String(max_length=250, comment="test that it works")
@pytest.fixture(autouse=True, scope="module")
def create_test_database():
engine = sqlalchemy.create_engine(DATABASE_URL)
metadata.create_all(engine)
yield
metadata.drop_all(engine)
create_test_database = init_tests(base_ormar_config)
@pytest.mark.asyncio
async def test_comments_are_set_in_db():
columns = Comment.Meta.table.c
columns = Comment.ormar_config.table.c
for c in columns:
assert c.comment == Comment.Meta.model_fields[c.name].comment
assert c.comment == Comment.ormar_config.model_fields[c.name].comment