Skip to content

Commit e70532d

Browse files
committed
➕ Add metaclass testing
1 parent 4ea9261 commit e70532d

File tree

8 files changed

+131
-15
lines changed

8 files changed

+131
-15
lines changed

saffier/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from saffier.core.registry import Registry
44
from saffier.db.constants import CASCADE, RESTRICT, SET_NULL
55
from saffier.db.manager import Manager
6+
from saffier.db.queryset import QuerySet
67
from saffier.exceptions import DoesNotFound, MultipleObjectsReturned
78
from saffier.fields import (
89
BigIntegerField,
@@ -43,6 +44,7 @@
4344
"Model",
4445
"MultipleObjectsReturned",
4546
"OneToOneField",
47+
"QuerySet",
4648
"RESTRICT",
4749
"Registry",
4850
"SET_NULL",

saffier/exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ def __str__(self) -> str:
2121
return "".join(self.args).strip()
2222

2323

24-
class DoesNotFound(Exception):
24+
class DoesNotFound(SaffierException):
2525
...
2626

2727

28-
class MultipleObjectsReturned(Exception):
28+
class MultipleObjectsReturned(SaffierException):
2929
...
3030

3131

saffier/metaclass.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ def __search_for_fields(base: typing.Type, attrs: DictAny) -> None:
164164

165165
if not isinstance(attrs["id"], Field) or not attrs["id"].primary_key:
166166
raise ImproperlyConfigured(
167-
f"Cannot create model {name} without explicit primary key if field 'id' is already present"
167+
f"Cannot create model {name} without explicit primary key if field 'id' is already present."
168168
)
169169

170170
for key, value in attrs.items():
@@ -216,6 +216,14 @@ def __search_for_fields(base: typing.Type, attrs: DictAny) -> None:
216216
registry = meta.registry
217217
new_class.database = registry.database
218218

219+
# Abstract classes do not allow multiple managers. This make sure it is enforced.
220+
if meta.abstract:
221+
managers = [k for k, v in attrs.items() if isinstance(v, Manager)]
222+
if len(managers) > 1:
223+
raise ImproperlyConfigured(
224+
"Multiple managers are not allowed in abstract classes."
225+
)
226+
219227
# Making sure it does not generate tables if abstract it set
220228
if not meta.abstract:
221229
registry.models[name] = new_class

tests/managers/test_managers_abstract.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,12 @@ async def rollback_connections():
6363
yield
6464

6565

66-
async def test_inherited_abstract_base_model_managers():
66+
@pytest.mark.parametrize("manager,total", [("query", 4), ("languages", 2)])
67+
async def test_inherited_abstract_base_model_managers(manager, total):
6768
await HubUser.query.create(name="test", language="EN")
6869
await HubUser.query.create(name="test2", language="EN")
6970
await HubUser.query.create(name="test3", language="PT")
7071
await HubUser.query.create(name="test4", language="PT")
7172

72-
# users = await HubUser.query.all()
73-
# assert len(users) == 4
74-
75-
users = await HubUser.languages.all()
76-
assert len(users) == 2
73+
users = await getattr(HubUser, manager).all()
74+
assert len(users) == total
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import pytest
2+
from tests.settings import DATABASE_URL
3+
4+
import saffier
5+
from saffier import Manager
6+
from saffier.db.connection import Database
7+
from saffier.db.queryset import QuerySet
8+
from saffier.exceptions import ImproperlyConfigured
9+
10+
database = Database(url=DATABASE_URL)
11+
models = saffier.Registry(database=database)
12+
13+
pytestmark = pytest.mark.anyio
14+
15+
16+
class ObjectsManager(Manager):
17+
def get_queryset(self) -> QuerySet:
18+
queryset = super().get_queryset().filter(is_active=True)
19+
return queryset
20+
21+
22+
class LanguageManager(Manager):
23+
def get_queryset(self) -> QuerySet:
24+
queryset = super().get_queryset().filter(language="EN")
25+
return queryset
26+
27+
28+
async def test_inherited_abstract_base_model_managers_raises_error_on_multiple():
29+
with pytest.raises(ImproperlyConfigured) as raised:
30+
31+
class BaseModel(saffier.Model):
32+
query = ObjectsManager()
33+
languages = LanguageManager()
34+
35+
class Meta:
36+
abstract = True
37+
registry = models
38+
39+
assert raised.value.args[0] == "Multiple managers are not allowed in abstract classes."

tests/managers/test_managers_inherited.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,17 @@ async def test_inherited_base_model_managers():
8181
assert len(users) == 2
8282

8383

84-
async def test_inherited_base_model_managers_product():
84+
@pytest.mark.parametrize("manager,total", [("query", 6), ("ratings", 3)])
85+
async def test_inherited_base_model_managers_product(manager, total):
8586
await Product.query.create(name="test", rating=5)
8687
await Product.query.create(name="test2", rating=4)
8788
await Product.query.create(name="test3", rating=3)
8889
await Product.query.create(name="test4", rating=2)
8990
await Product.query.create(name="test5", rating=2)
9091
await Product.query.create(name="test6", rating=1)
9192

92-
products = await Product.query.all()
93-
assert len(products) == 6
94-
95-
products = await Product.ratings.all()
96-
assert len(products) == 3
93+
products = await getattr(Product, manager).all()
94+
assert len(products) == total
9795

9896

9997
async def test_raises_key_error_on_non_existing_field_for_product():
File renamed without changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import pytest
2+
from tests.settings import DATABASE_URL
3+
4+
import saffier
5+
from saffier import Manager, QuerySet
6+
from saffier.db.connection import Database
7+
from saffier.exceptions import ImproperlyConfigured
8+
9+
pytestmark = pytest.mark.anyio
10+
11+
database = Database(DATABASE_URL)
12+
models = saffier.Registry(database=database)
13+
14+
15+
class User(saffier.Model):
16+
id = saffier.IntegerField(primary_key=True)
17+
name = saffier.CharField(max_length=100)
18+
19+
class Meta:
20+
registry = models
21+
22+
23+
class ObjectsManager(Manager):
24+
def get_queryset(self) -> QuerySet:
25+
queryset = super().get_queryset().filter(name__icontains="a")
26+
return queryset
27+
28+
29+
async def test_improperly_configured_for_multiple_managers_on_abstract_class():
30+
with pytest.raises(ImproperlyConfigured) as raised:
31+
32+
class BaseModel(saffier.Model):
33+
query = ObjectsManager()
34+
languages = ObjectsManager()
35+
36+
class Meta:
37+
abstract = True
38+
registry = models
39+
40+
assert raised.value.args[0] == "Multiple managers are not allowed in abstract classes."
41+
42+
43+
async def test_improperly_configured_for_primary_key():
44+
with pytest.raises(ImproperlyConfigured) as raised:
45+
46+
class BaseModel(saffier.Model):
47+
id = saffier.IntegerField(primary_key=False)
48+
query = ObjectsManager()
49+
languages = ObjectsManager()
50+
51+
class Meta:
52+
registry = models
53+
54+
assert (
55+
raised.value.args[0]
56+
== "Cannot create model BaseModel without explicit primary key if field 'id' is already present."
57+
)
58+
59+
60+
async def test_improperly_configured_for_multiple_primary_keys():
61+
with pytest.raises(ImproperlyConfigured) as raised:
62+
63+
class BaseModel(saffier.Model):
64+
name = saffier.IntegerField(primary_key=True)
65+
query = ObjectsManager()
66+
languages = ObjectsManager()
67+
68+
class Meta:
69+
registry = models
70+
71+
assert raised.value.args[0] == "Cannot create model BaseModel with multiple primary keys."

0 commit comments

Comments
 (0)