add is null filter, add complex fiters (and_ & or_) and basic tests for them
This commit is contained in:
149
tests/test_filter_groups.py
Normal file
149
tests/test_filter_groups.py
Normal file
@ -0,0 +1,149 @@
|
||||
from typing import Optional
|
||||
|
||||
import databases
|
||||
import sqlalchemy
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
database = databases.Database(DATABASE_URL)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class Author(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "authors"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "books"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||
title: str = ormar.String(max_length=100)
|
||||
year: int = ormar.Integer(nullable=True)
|
||||
|
||||
|
||||
def test_or_group():
|
||||
result = ormar.or_(name="aa", books__title="bb")
|
||||
result.resolve(model_cls=Author)
|
||||
assert len(result.actions) == 2
|
||||
assert result.actions[0].target_model == Author
|
||||
assert result.actions[1].target_model == Book
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( authors.name = 'aa' OR "
|
||||
f"{result.actions[1].table_prefix}"
|
||||
f"_books.title = 'bb' )"
|
||||
)
|
||||
assert not result.is_source_model_filter
|
||||
|
||||
|
||||
def test_and_group():
|
||||
result = ormar.and_(name="aa", books__title="bb")
|
||||
result.resolve(model_cls=Author)
|
||||
assert len(result.actions) == 2
|
||||
assert result.actions[0].target_model == Author
|
||||
assert result.actions[1].target_model == Book
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( authors.name = 'aa' AND "
|
||||
f"{result.actions[1].table_prefix}"
|
||||
f"_books.title = 'bb' )"
|
||||
)
|
||||
assert not result.is_source_model_filter
|
||||
|
||||
|
||||
def test_nested_and():
|
||||
result = ormar.and_(
|
||||
ormar.or_(name="aa", books__title="bb"), ormar.or_(name="cc", books__title="dd")
|
||||
)
|
||||
result.resolve(model_cls=Author)
|
||||
assert len(result.actions) == 0
|
||||
assert len(result._nested_groups) == 2
|
||||
book_prefix = result._nested_groups[0].actions[1].table_prefix
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( ( authors.name = 'aa' OR "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'bb' ) AND "
|
||||
f"( authors.name = 'cc' OR "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'dd' ) )"
|
||||
)
|
||||
assert not result.is_source_model_filter
|
||||
|
||||
|
||||
def test_nested_group_and_action():
|
||||
result = ormar.and_(ormar.or_(name="aa", books__title="bb"), books__title="dd")
|
||||
result.resolve(model_cls=Author)
|
||||
assert len(result.actions) == 1
|
||||
assert len(result._nested_groups) == 1
|
||||
book_prefix = result._nested_groups[0].actions[1].table_prefix
|
||||
assert (
|
||||
str(result.get_text_clause()) == f"( ( authors.name = 'aa' OR "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'bb' ) AND "
|
||||
f"{book_prefix}"
|
||||
f"_books.title = 'dd' )"
|
||||
)
|
||||
assert not result.is_source_model_filter
|
||||
|
||||
|
||||
def test_deeply_nested_or():
|
||||
result = ormar.or_(
|
||||
ormar.and_(
|
||||
ormar.or_(name="aa", books__title="bb"),
|
||||
ormar.or_(name="cc", books__title="dd"),
|
||||
),
|
||||
ormar.and_(
|
||||
ormar.or_(books__year__lt=1900, books__title="11"),
|
||||
ormar.or_(books__year__gt="xx", books__title="22"),
|
||||
),
|
||||
)
|
||||
result.resolve(model_cls=Author)
|
||||
assert len(result.actions) == 0
|
||||
assert len(result._nested_groups) == 2
|
||||
assert len(result._nested_groups[0]._nested_groups) == 2
|
||||
book_prefix = result._nested_groups[0]._nested_groups[0].actions[1].table_prefix
|
||||
result_qry = str(result.get_text_clause())
|
||||
expected_qry = (
|
||||
f"( ( ( authors.name = 'aa' OR {book_prefix}_books.title = 'bb' ) AND "
|
||||
f"( authors.name = 'cc' OR {book_prefix}_books.title = 'dd' ) ) "
|
||||
f"OR ( ( {book_prefix}_books.year < 1900 OR {book_prefix}_books.title = '11' ) AND "
|
||||
f"( {book_prefix}_books.year > 'xx' OR {book_prefix}_books.title = '22' ) ) )"
|
||||
)
|
||||
assert result_qry.replace("\n", "") == expected_qry.replace("\n", "")
|
||||
assert not result.is_source_model_filter
|
||||
|
||||
|
||||
def test_one_model_group():
|
||||
result = ormar.and_(year__gt=1900, title="bb")
|
||||
result.resolve(model_cls=Book)
|
||||
assert len(result.actions) == 2
|
||||
assert len(result._nested_groups) == 0
|
||||
assert result.is_source_model_filter
|
||||
|
||||
|
||||
def test_one_model_nested_group():
|
||||
result = ormar.and_(
|
||||
ormar.or_(year__gt=1900, title="bb"), ormar.or_(year__lt=1800, title="aa")
|
||||
)
|
||||
result.resolve(model_cls=Book)
|
||||
assert len(result.actions) == 0
|
||||
assert len(result._nested_groups) == 2
|
||||
assert result.is_source_model_filter
|
||||
|
||||
|
||||
def test_one_model_with_group():
|
||||
result = ormar.or_(ormar.and_(year__gt=1900, title="bb"), title="uu")
|
||||
result.resolve(model_cls=Book)
|
||||
assert len(result.actions) == 1
|
||||
assert len(result._nested_groups) == 1
|
||||
assert result.is_source_model_filter
|
||||
76
tests/test_isnull_filter.py
Normal file
76
tests/test_isnull_filter.py
Normal file
@ -0,0 +1,76 @@
|
||||
from typing import Optional
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
database = databases.Database(DATABASE_URL)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class Author(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "authors"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "books"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||
title: str = ormar.String(max_length=100)
|
||||
year: int = ormar.Integer(nullable=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.drop_all(engine)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_is_null():
|
||||
async with database:
|
||||
tolkien = await Author.objects.create(name="J.R.R. Tolkien")
|
||||
await Book.objects.create(author=tolkien, title="The Hobbit")
|
||||
await Book.objects.create(
|
||||
author=tolkien, title="The Lord of the Rings", year=1955
|
||||
)
|
||||
await Book.objects.create(author=tolkien, title="The Silmarillion", year=1977)
|
||||
|
||||
books = await Book.objects.all(year__isnull=True)
|
||||
assert len(books) == 1
|
||||
assert books[0].year is None
|
||||
assert books[0].title == "The Hobbit"
|
||||
|
||||
books = await Book.objects.all(year__isnull=False)
|
||||
assert len(books) == 2
|
||||
|
||||
tolkien = await Author.objects.select_related("books").get(
|
||||
books__year__isnull=True
|
||||
)
|
||||
assert len(tolkien.books) == 1
|
||||
assert tolkien.books[0].year is None
|
||||
assert tolkien.books[0].title == "The Hobbit"
|
||||
|
||||
tolkien = await Author.objects.select_related("books").get(
|
||||
books__year__isnull=False
|
||||
)
|
||||
assert len(tolkien.books) == 2
|
||||
assert tolkien.books[0].year == 1955
|
||||
assert tolkien.books[0].title == "The Lord of the Rings"
|
||||
118
tests/test_or_filters.py
Normal file
118
tests/test_or_filters.py
Normal file
@ -0,0 +1,118 @@
|
||||
from typing import Optional
|
||||
|
||||
import databases
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
|
||||
import ormar
|
||||
from tests.settings import DATABASE_URL
|
||||
|
||||
database = databases.Database(DATABASE_URL)
|
||||
metadata = sqlalchemy.MetaData()
|
||||
|
||||
|
||||
class BaseMeta(ormar.ModelMeta):
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
|
||||
class Author(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "authors"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
name: str = ormar.String(max_length=100)
|
||||
|
||||
|
||||
class Book(ormar.Model):
|
||||
class Meta(BaseMeta):
|
||||
tablename = "books"
|
||||
|
||||
id: int = ormar.Integer(primary_key=True)
|
||||
author: Optional[Author] = ormar.ForeignKey(Author)
|
||||
title: str = ormar.String(max_length=100)
|
||||
year: int = ormar.Integer(nullable=True)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope="module")
|
||||
def create_test_database():
|
||||
engine = sqlalchemy.create_engine(DATABASE_URL)
|
||||
metadata.drop_all(engine)
|
||||
metadata.create_all(engine)
|
||||
yield
|
||||
metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_or_filters():
|
||||
async with database:
|
||||
tolkien = await Author(name="J.R.R. Tolkien").save()
|
||||
await Book(author=tolkien, title="The Hobbit", year=1933).save()
|
||||
await Book(author=tolkien, title="The Lord of the Rings", year=1955).save()
|
||||
await Book(author=tolkien, title="The Silmarillion", year=1977).save()
|
||||
sapkowski = await Author(name="Andrzej Sapkowski").save()
|
||||
await Book(author=sapkowski, title="The Witcher", year=1990).save()
|
||||
await Book(author=sapkowski, title="The Tower of Fools", year=2002).save()
|
||||
|
||||
books = (
|
||||
await Book.objects.select_related("author")
|
||||
.filter(ormar.or_(author__name="J.R.R. Tolkien", year__gt=1970))
|
||||
.all()
|
||||
)
|
||||
assert len(books) == 5
|
||||
|
||||
books = (
|
||||
await Book.objects.select_related("author")
|
||||
.filter(ormar.or_(author__name="J.R.R. Tolkien", year__lt=1995))
|
||||
.all()
|
||||
)
|
||||
assert len(books) == 4
|
||||
assert not any([x.title == "The Tower of Fools" for x in books])
|
||||
|
||||
books = (
|
||||
await Book.objects.select_related("author")
|
||||
.filter(ormar.or_(year__gt=1960, year__lt=1940))
|
||||
.filter(author__name="J.R.R. Tolkien")
|
||||
.all()
|
||||
)
|
||||
assert len(books) == 2
|
||||
assert books[0].title == "The Hobbit"
|
||||
assert books[1].title == "The Silmarillion"
|
||||
|
||||
books = (
|
||||
await Book.objects.select_related("author")
|
||||
.filter(
|
||||
ormar.or_(
|
||||
ormar.and_(year__gt=1960, author__name="J.R.R. Tolkien"),
|
||||
ormar.and_(year__lt=2000, author__name="Andrzej Sapkowski"),
|
||||
)
|
||||
)
|
||||
.filter(title__startswith="The")
|
||||
.all()
|
||||
)
|
||||
assert len(books) == 2
|
||||
assert books[0].title == "The Silmarillion"
|
||||
assert books[1].title == "The Witcher"
|
||||
|
||||
books = (
|
||||
await Book.objects.select_related("author")
|
||||
.exclude(
|
||||
ormar.or_(
|
||||
ormar.and_(year__gt=1960, author__name="J.R.R. Tolkien"),
|
||||
ormar.and_(year__lt=2000, author__name="Andrzej Sapkowski"),
|
||||
)
|
||||
)
|
||||
.filter(title__startswith="The")
|
||||
.all()
|
||||
)
|
||||
assert len(books) == 3
|
||||
assert not any([x.title in ["The Silmarillion", "The Witcher"] for x in books])
|
||||
|
||||
|
||||
# TODO: Check / modify
|
||||
# process and and or into filter groups (V)
|
||||
# check exclude queries working (V)
|
||||
|
||||
# when limit and no sql do not allow main model and other models
|
||||
# check complex prefixes properly resolved
|
||||
# fix types for FilterAction and FilterGroup (?)
|
||||
Reference in New Issue
Block a user