Skip to content
Closed
20 changes: 10 additions & 10 deletions docs/user-guide/api/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async def get_user(
):
"""Get a user by ID."""
user = await crud_users.get(db=db, id=user_id, schema_to_select=UserRead)
if not user:
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
```
Expand All @@ -42,7 +42,7 @@ async def get_user(
db: Annotated[AsyncSession, Depends(async_get_db)]
):
user = await crud_users.get(db=db, id=user_id, schema_to_select=UserRead)
if not user:
if user is None:
raise HTTPException(status_code=404, detail="User not found")
return user
```
Expand Down Expand Up @@ -82,7 +82,7 @@ async def create_user(
db: Annotated[AsyncSession, Depends(async_get_db)]
):
# Check if user already exists
if await crud_users.exists(db=db, email=user_data.email):
if await crud_users.exists(db=db, email=user_data.email) is True:
Copy link
Collaborator

@igorbenav igorbenav Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://peps.python.org/pep-0008/#programming-recommendations

if x is preferred to if x == True and if x is True

raise HTTPException(status_code=409, detail="Email already exists")

# Create user
Expand All @@ -100,7 +100,7 @@ async def update_user(
db: Annotated[AsyncSession, Depends(async_get_db)]
):
# Check if user exists
if not await crud_users.exists(db=db, id=user_id):
if await crud_users.exists(db=db, id=user_id) is None:
Copy link
Collaborator

@igorbenav igorbenav Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exists returns a boolean

raise HTTPException(status_code=404, detail="User not found")

# Update user
Expand All @@ -116,7 +116,7 @@ async def delete_user(
user_id: int,
db: Annotated[AsyncSession, Depends(async_get_db)]
):
if not await crud_users.exists(db=db, id=user_id):
if await crud_users.exists(db=db, id=user_id) is None:
Copy link
Collaborator

@igorbenav igorbenav Oct 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exists returns a boolean

raise HTTPException(status_code=404, detail="User not found")

await crud_users.delete(db=db, id=user_id)
Expand Down Expand Up @@ -176,9 +176,9 @@ async def search_users(
db: Annotated[AsyncSession, Depends(async_get_db)]
):
filters = {"is_active": is_active}
if name:
if name is True:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work only if name is a boolean, which it isn't. It's breaking logic

filters["name"] = name
if age:
if age is True:
filters["age"] = age

users = await crud_users.get_multi(db=db, **filters)
Expand Down Expand Up @@ -220,13 +220,13 @@ from app.core.exceptions.http_exceptions import (
@router.get("/{user_id}")
async def get_user(user_id: int, db: AsyncSession):
user = await crud_users.get(db=db, id=user_id)
if not user:
if user is None:
raise NotFoundException("User not found") # Returns 404
return user

@router.post("/")
async def create_user(user_data: UserCreate, db: AsyncSession):
if await crud_users.exists(db=db, email=user_data.email):
if await crud_users.exists(db=db, email=user_data.email) is True:
raise DuplicateValueException("Email already exists") # Returns 409

return await crud_users.create(db=db, object=user_data)
Expand All @@ -245,7 +245,7 @@ async def upload_avatar(
db: Annotated[AsyncSession, Depends(async_get_db)]
):
# Check file type
if not file.content_type.startswith('image/'):
if file.content_type is None or file.content_type.startswith("image/") is False:
raise HTTPException(status_code=400, detail="File must be an image")

# Save file and update user
Expand Down
44 changes: 22 additions & 22 deletions docs/user-guide/api/exceptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ from app.core.exceptions.http_exceptions import NotFoundException
@router.get("/{user_id}")
async def get_user(user_id: int, db: AsyncSession):
user = await crud_users.get(db=db, id=user_id)
if not user:
if user is None:
raise NotFoundException("User not found") # Returns 404
return user
```
Expand All @@ -30,7 +30,7 @@ from app.core.exceptions.http_exceptions import NotFoundException
@router.get("/{user_id}")
async def get_user(user_id: int):
user = await crud_users.get(db=db, id=user_id)
if not user:
if user is None:
raise NotFoundException("User not found")
return user

Expand All @@ -45,7 +45,7 @@ from app.core.exceptions.http_exceptions import DuplicateValueException

@router.post("/")
async def create_user(user_data: UserCreate):
if await crud_users.exists(db=db, email=user_data.email):
if await crud_users.exists(db=db, email=user_data.email) is True:
raise DuplicateValueException("Email already exists")

return await crud_users.create(db=db, object=user_data)
Expand All @@ -64,7 +64,7 @@ async def delete_user(
user_id: int,
current_user: Annotated[dict, Depends(get_current_user)]
):
if current_user["id"] != user_id and not current_user["is_superuser"]:
if current_user["id"] != user_id and current_user["is_superuser"] is False:
raise ForbiddenException("You can only delete your own account")

await crud_users.delete(db=db, id=user_id)
Expand All @@ -83,7 +83,7 @@ from app.core.exceptions.http_exceptions import UnauthorizedException
@router.get("/admin-only")
async def admin_endpoint():
# Some validation logic
if not user_is_admin:
if user_is_admin is False:
raise UnauthorizedException("Admin access required")

return {"data": "secret admin data"}
Expand All @@ -100,11 +100,11 @@ async def admin_endpoint():
@router.post("/", response_model=UserRead)
async def create_user(user_data: UserCreate, db: AsyncSession):
# Check email
if await crud_users.exists(db=db, email=user_data.email):
if await crud_users.exists(db=db, email=user_data.email) is True:
raise DuplicateValueException("Email already exists")

# Check username
if await crud_users.exists(db=db, username=user_data.username):
if await crud_users.exists(db=db, username=user_data.username) is True:
raise DuplicateValueException("Username already taken")

# Create user
Expand All @@ -123,13 +123,13 @@ async def update_user(
db: AsyncSession
):
# Check if user exists
if not await crud_users.exists(db=db, id=user_id):
if await crud_users.exists(db=db, id=user_id) is None:
raise NotFoundException("User not found")

# Check for email conflicts (if email is being updated)
if user_data.email:
if user_data.email is True:
existing = await crud_users.get(db=db, email=user_data.email)
if existing and existing.id != user_id:
if existing is True and existing.id != user_id:
raise DuplicateValueException("Email already taken")

# Update user
Expand All @@ -145,11 +145,11 @@ async def get_post(
db: AsyncSession
):
post = await crud_posts.get(db=db, id=post_id)
if not post:
if post is None:
raise NotFoundException("Post not found")

# Check if user owns the post or is admin
if post.author_id != current_user["id"] and not current_user["is_superuser"]:
if post.author_id != current_user["id"] and current_user["is_superuser"] is False:
raise ForbiddenException("You can only view your own posts")

return post
Expand Down Expand Up @@ -187,7 +187,7 @@ from fastapi import HTTPException
# Bad Request (400)
@router.post("/")
async def create_something(data: dict):
if not data.get("required_field"):
if data.get("required_field") is None:
raise HTTPException(
status_code=400,
detail="required_field is missing"
Expand All @@ -196,7 +196,7 @@ async def create_something(data: dict):
# Too Many Requests (429)
@router.post("/")
async def rate_limited_endpoint():
if rate_limit_exceeded():
if rate_limit_exceeded() is True:
raise HTTPException(
status_code=429,
detail="Rate limit exceeded. Try again later."
Expand Down Expand Up @@ -315,13 +315,13 @@ async def login(credentials: LoginCredentials):
user = await crud_users.get(db=db, username=credentials.username)

# Don't do this - reveals if username exists
# if not user:
# if user is None:
# raise NotFoundException("User not found")
# if not verify_password(credentials.password, user.hashed_password):
# if verify_password(credentials.password, user.hashed_password) is False:
# raise UnauthorizedException("Invalid password")

# Do this - generic message for all auth failures
if not user or not verify_password(credentials.password, user.hashed_password):
if user is None or verify_password(credentials.password, user.hashed_password) is False:
raise UnauthorizedException("Invalid username or password")

return create_access_token(user.id)
Expand All @@ -332,11 +332,11 @@ async def forgot_password(email: str):
user = await crud_users.get(db=db, email=email)

# Don't do this - reveals if email exists
# if not user:
# if user is None:
# raise NotFoundException("Email not found")

# Do this - always return success message
if user:
if user is True:
await send_password_reset_email(user.email)

# Always return the same message
Expand All @@ -355,7 +355,7 @@ async def get_post(
current_user: Annotated[dict, Depends(get_current_user)]
):
post = await crud_posts.get(db=db, id=post_id)
if not post:
if post is None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with the other one because if we change it in the future to return something else, None will not work. Maybe some people would like to comment on this

Copy link
Contributor

@carlosplanchon carlosplanchon Oct 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think not should be kept in case falsy values are expected in the future.
However, a warning notice should be added to the website (in exceptions.md) to clearly inform users about this behavior, specifically, to make them aware of possible future changes in fast-crud regarding this.

raise NotFoundException("Post not found") # Safe to be specific

if post.author_id != current_user["id"]:
Expand All @@ -370,7 +370,7 @@ async def get_post(
### 1. Use Specific Exceptions (When Safe)
```python
# Good for non-sensitive operations
if not user:
if user is None:
raise NotFoundException("User not found")

# Good for validation errors
Expand Down Expand Up @@ -398,7 +398,7 @@ async def delete_user(
raise ForbiddenException("Cannot delete other users")

# Then check if user exists
if not await crud_users.exists(db=db, id=user_id):
if await crud_users.exists(db=db, id=user_id) is False:
raise NotFoundException("User not found")

await crud_users.delete(db=db, id=user_id)
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ from app.core.exceptions.http_exceptions import NotFoundException
@router.get("/{user_id}")
async def get_user(user_id: int):
user = await crud_users.get(id=user_id)
if not user:
if user is None:
raise NotFoundException("User not found") # Returns proper 404
return user
```
Expand Down
8 changes: 4 additions & 4 deletions docs/user-guide/api/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async def get_users(
):
# Build filters
filters = {}
if search:
if search is True:
filters["name__icontains"] = search # Search by name
if is_active is not None:
filters["is_active"] = is_active
Expand Down Expand Up @@ -185,12 +185,12 @@ async def get_users(

if is_active is not None:
filters["is_active"] = is_active
if tier_id:
if tier_id is True:
filters["tier_id"] = tier_id

# Handle search
search_criteria = []
if search:
if search is True:
from sqlalchemy import or_, func
search_criteria = [
or_(
Expand Down Expand Up @@ -280,7 +280,7 @@ async def get_all_users_admin(
db: Annotated[AsyncSession, Depends(async_get_db)]
):
filters = {}
if not include_deleted:
if include_deleted is False:
filters["is_deleted"] = False

users = await crud_users.get_multi(db=db, **filters)
Expand Down
4 changes: 2 additions & 2 deletions docs/user-guide/api/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,9 @@ import logging
logger = logging.getLogger(__name__)

async def version_tracking_middleware(request: Request, call_next):
if request.url.path.startswith("/api/v1/"):
if request.url.path.startswith("/api/v1/") is True:
logger.info(f"v1 usage: {request.method} {request.url.path}")
elif request.url.path.startswith("/api/v2/"):
elif request.url.path.startswith("/api/v2/") is True:
logger.info(f"v2 usage: {request.method} {request.url.path}")

response = await call_next(request)
Expand Down
6 changes: 3 additions & 3 deletions docs/user-guide/authentication/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ async def protected_endpoint(current_user: dict = Depends(get_current_user)):
# Optional authentication
@router.get("/public")
async def public_endpoint(user: dict | None = Depends(get_optional_user)):
if user:
if user is True:
return {"premium_content": True}
return {"premium_content": False}

Expand All @@ -76,7 +76,7 @@ async def update_post(post_id: int, current_user: dict = Depends(get_current_use
post = await crud_posts.get(db=db, id=post_id)

# Check ownership or admin privileges
if post["created_by_user_id"] != current_user["id"] and not current_user["is_superuser"]:
if post["created_by_user_id"] != current_user["id"] and current_user["is_superuser"] is False:
raise ForbiddenException("Cannot update other users' posts")

return await crud_posts.update(db=db, id=post_id, object=updates)
Expand Down Expand Up @@ -148,7 +148,7 @@ async def get_my_data(current_user: dict = Depends(get_current_user)):

# Check user permissions
def check_tier_access(user: dict, required_tier: str):
if not user.get("tier") or user["tier"]["name"] != required_tier:
if user.get("tier") is None or user["tier"]["name"] != required_tier:
raise ForbiddenException(f"Requires {required_tier} tier")

# Custom authentication dependency
Expand Down
Loading