Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ laravel-repo
application
.agents
.uv_cache
.claude/
5 changes: 5 additions & 0 deletions example/config-app/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 12 additions & 2 deletions example/console-app/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions example/database-app/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion example/fastapi-app/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 21 additions & 2 deletions example/vite-app/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,5 @@
ValidationException,
AmbiguousError,
MethodNotAllowedException,
ModelNotFoundException,
ThrottleRequestsException,
)
10 changes: 0 additions & 10 deletions fastapi_startkit/src/fastapi_startkit/exceptions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,6 @@ class NotificationException(Exception):
pass


class ModelNotFoundException(Exception):
is_http_exception = True

def get_response(self):
return "Model Not Found"

def get_status(self):
return 404


class AuthorizationException(Exception):
is_http_exception = True

Expand Down
2 changes: 2 additions & 0 deletions fastapi_startkit/src/fastapi_startkit/masoniteorm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .config.config import MySQLConfig, PostgresConfig, SQLiteConfig
from .exceptions import ModelNotFoundException
from .facades import DB
from .migrations.Migration import Migration
from .migrations.Migrator import Migrator
Expand All @@ -13,6 +14,7 @@
"MySQLConfig",
"SQLiteConfig",
"Model",
"ModelNotFoundException",
"DB",
"Migration",
"Migrator",
Expand Down
12 changes: 12 additions & 0 deletions fastapi_startkit/src/fastapi_startkit/masoniteorm/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class ModelNotFoundException(Exception):
is_http_exception = True

def __init__(self, message="Model Not Found"):
super().__init__(message)
self.message = message

def get_response(self):
return self.message

def get_status(self):
return 404
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ def limit(self, limit: int) -> "QueryBuilder":
async def find(self, primary_key: str | int, columns=None):
return await self.where(self._model.__primary_key__, primary_key).first(columns)

async def find_or_fail(self, primary_key: str | int, columns=None):
from fastapi_startkit.masoniteorm.exceptions import ModelNotFoundException

result = await self.find(primary_key, columns)
if result is None:
raise ModelNotFoundException(
f"{self._model.__class__.__name__} with primary key {primary_key!r} not found."
)
return result

async def first(self, columns=None):
if not columns:
columns = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ def or_where_has(cls, relation: str, callback=None) -> "QueryBuilder":
async def find(cls, primary_key: str | int, columns=None):
return await cls.query().find(primary_key, columns)

@classmethod
async def find_or_fail(cls, primary_key: str | int, columns=None):
return await cls.query().find_or_fail(primary_key, columns)

@classmethod
async def first(cls, columns=None):
return await cls.query().first(columns)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ..fixtures.db import DB
from ...fixtures.model import User
from ..test_case import TestCase
from fastapi_startkit.masoniteorm import ModelNotFoundException


class SqliteTestQueryBuilderModel(TestCase):
Expand Down Expand Up @@ -43,6 +44,27 @@ async def test_can_find_list(self):
self.assertEqual(sql, 'SELECT * FROM "users" WHERE "users"."id" = ? LIMIT 1')
self.assertIn(1, bindings)

async def test_find_or_fail_returns_model_when_found(self):
user = await User.find_or_fail(1)
self.assertIsInstance(user, User)
self.assertEqual(user.id, 1)

async def test_find_or_fail_raises_when_not_found(self):
with self.assertRaises(ModelNotFoundException):
await User.find_or_fail(99999)

async def test_find_or_fail_exception_has_404_status(self):
try:
await User.find_or_fail(99999)
except ModelNotFoundException as e:
self.assertEqual(e.get_status(), 404)

async def test_find_or_fail_exception_message_contains_key(self):
try:
await User.find_or_fail(99999)
except ModelNotFoundException as e:
self.assertIn("99999", str(e))

async def test_can_set_and_retrieve_attribute(self):
user = await User.first()
user.name = "updated"
Expand Down
Loading