Update generated backend for blog_app with entities: posts, comments, tags, user

This commit is contained in:
Idris Abdurrahman 2025-03-21 09:48:28 +01:00
parent a10682022a
commit 11add1929b
18 changed files with 532 additions and 10 deletions

View File

@ -0,0 +1,8 @@
from sqlalchemy.orm import Session
from app.api.db.database import SessionLocal
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

View File

@ -0,0 +1,12 @@
import time
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
from loguru import logger
class ActivityTrackerMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
logger.info(f'{request.method} {request.url} - Process Time: {process_time:.6f} seconds')
return response

7
app/api/db/database.py Normal file
View File

@ -0,0 +1,7 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = 'sqlite:///./blog_app.db'
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={'check_same_thread': False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

View File

@ -0,0 +1,28 @@
Here's the `comments.py` file for the `app/api/v1/models/` directory of the `blog_app_svkgt` FastAPI backend, defining a SQLAlchemy model for comments:
from sqlalchemy import Column, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship
from app.db import Base
class Comment(Base):
__tablename__ = "comments"
id = Column(Integer, primary_key=True, index=True)
content = Column(Text, nullable=False)
author = Column(String, nullable=False)
post_id = Column(Integer, ForeignKey("posts.id"), nullable=False)
post = relationship("Post", back_populates="comments")
def __repr__(self):
return f"Comment(id={self.id}, content='{self.content[:20]}...', author='{self.author}', post_id={self.post_id})"
This code defines a `Comment` model that inherits from the `Base` class provided by SQLAlchemy. The `Comment` model has the following fields:
This model assumes that there is a `Post` model defined elsewhere in the project, which has a corresponding one-to-many relationship with the `Comment` model (i.e., one post can have multiple comments).
Make sure to import the necessary modules (`sqlalchemy` and `app.db`) at the beginning of the file, and update the import paths if necessary to match your project structure.

View File

@ -0,0 +1,31 @@
Here's the `posts.py` file for the `blog_app_svkgt` FastAPI backend, defining a SQLAlchemy model for posts:
from sqlalchemy import Column, Integer, String, Text, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql.sqltypes import TIMESTAMP
from sqlalchemy.sql import func
from app.db import Base
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, nullable=False)
content = Column(Text, nullable=False)
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship("User", back_populates="posts")
def __repr__(self):
return f"Post(id={self.id}, title='{self.title}', content='{self.content[:20]}...')"
Explanation:
5. The `title` column is a `String` column marked as `nullable=False`, meaning it cannot be null.
6. The `content` column is a `Text` column marked as `nullable=False`, allowing for longer text content.
7. The `created_at` column is a `TIMESTAMP` column with a default value set to the current server time using `func.now()`.
8. The `updated_at` column is a `TIMESTAMP` column with a default value set to the current server time using `func.now()`, and it will be updated with the current time whenever the row is updated using `onupdate=func.now()`.

27
app/api/v1/models/tags.py Normal file
View File

@ -0,0 +1,27 @@
Here's the `tags.py` file with a SQLAlchemy model for tags:
from sqlalchemy import Column, Integer, String
from app.db.base_class import Base
class Tag(Base):
__tablename__ = "tags"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, index=True)
description = Column(String, nullable=True)
def __repr__(self):
return f"Tag(id={self.id}, name='{self.name}', description='{self.description}')"
Explanation:
1. We import the necessary modules from SQLAlchemy: `Column`, `Integer`, and `String`.
2. We also import the `Base` class from `app.db.base_class`, which is a common pattern in FastAPI projects using SQLAlchemy. This `Base` class is typically defined in a separate file (`base_class.py`) and serves as the base class for all SQLAlchemy models.
5. We define three columns for the `Tag` model:
- `id`: An integer primary key and index column for the tag's unique identifier.
- `name`: A string column for the tag's name, which must be unique and indexed.
- `description`: An optional string column for the tag's description, which can be `None`.
Note: Make sure to place this `tags.py` file in the `app/api/v1/models/` directory, following the specified project structure.

30
app/api/v1/models/user.py Normal file
View File

@ -0,0 +1,30 @@
Here's the `user.py` file for the `app/api/v1/models/` directory, defining a SQLAlchemy model for the User entity in the `blog_app_svkgt` FastAPI backend:
from sqlalchemy import Column, Integer, String, Boolean
from app.db.base_class import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True, nullable=False)
hashed_password = Column(String, nullable=False)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
full_name = Column(String, index=True)
def __repr__(self):
return f"User(id={self.id}, email={self.email}, full_name={self.full_name})"
Explanation:
3. The model has the following columns:
- `id`: An integer primary key column for uniquely identifying each user.
- `email`: A string column for storing the user's email address. It is set as unique and indexed for efficient lookups.
- `hashed_password`: A string column for storing the hashed password of the user.
- `is_active`: A boolean column indicating whether the user account is active or not. By default, it is set to `True`.
- `is_superuser`: A boolean column indicating whether the user has superuser privileges or not. By default, it is set to `False`.
- `full_name`: A string column for storing the user's full name. It is indexed for efficient lookups.
Note: This model assumes that you have a `Base` class defined in `app.db.base_class` module, which is typically created using the SQLAlchemy declarative base. You may need to adjust the import statement and the `Base` class according to your project's structure and database configuration.

View File

@ -0,0 +1,2 @@
from fastapi import APIRouter
router = APIRouter()

View File

@ -0,0 +1,55 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.db import get_db
from app.models import Comment
from app.schemas import CommentCreate, CommentResponse
router = APIRouter(
prefix="/comments",
tags=["Comments"],
@router.post("/", response_model=CommentResponse, status_code=status.HTTP_201_CREATED)
def create_comment(comment: CommentCreate, db: Session = Depends(get_db)):
new_comment = Comment(**comment.dict())
db.add(new_comment)
db.commit()
db.refresh(new_comment)
return new_comment
@router.get("/", response_model=List[CommentResponse])
def get_all_comments(db: Session = Depends(get_db)):
comments = db.query(Comment).all()
return comments
@router.get("/{comment_id}", response_model=CommentResponse)
def get_comment(comment_id: int, db: Session = Depends(get_db)):
comment = db.query(Comment).filter(Comment.id == comment_id).first()
if not comment:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
return comment
@router.put("/{comment_id}", response_model=CommentResponse)
def update_comment(comment_id: int, comment: CommentCreate, db: Session = Depends(get_db)):
db_comment = db.query(Comment).filter(Comment.id == comment_id).first()
if not db_comment:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
db_comment.text = comment.text
db.commit()
db.refresh(db_comment)
return db_comment
@router.delete("/{comment_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_comment(comment_id: int, db: Session = Depends(get_db)):
comment = db.query(Comment).filter(Comment.id == comment_id).first()
if not comment:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Comment not found")
db.delete(comment)
db.commit()
return
This code defines a `router` object with the prefix `/comments` and the tag `"Comments"`. The router contains the following endpoints:
Note: You'll need to define the `Comment`, `CommentCreate`, and `CommentResponse` models and schemas in the appropriate files (`models.py` and `schemas.py`) for this code to work correctly.

View File

@ -0,0 +1,57 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.db import get_db
from app.models import Post
from app.schemas import PostCreate, PostResponse
router = APIRouter(
prefix="/posts",
tags=["Posts"],
@router.post("/", response_model=PostResponse, status_code=status.HTTP_201_CREATED)
def create_post(post: PostCreate, db: Session = Depends(get_db)):
new_post = Post(**post.dict())
db.add(new_post)
db.commit()
db.refresh(new_post)
return new_post
@router.get("/", response_model=List[PostResponse])
def get_all_posts(db: Session = Depends(get_db)):
posts = db.query(Post).all()
return posts
@router.get("/{post_id}", response_model=PostResponse)
def get_post(post_id: int, db: Session = Depends(get_db)):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Post not found")
return post
@router.put("/{post_id}", response_model=PostResponse)
def update_post(post_id: int, post: PostCreate, db: Session = Depends(get_db)):
db_post = db.query(Post).filter(Post.id == post_id).first()
if not db_post:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Post not found")
update_data = post.dict(exclude_unset=True)
for key, value in update_data.items():
setattr(db_post, key, value)
db.commit()
db.refresh(db_post)
return db_post
@router.delete("/{post_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_post(post_id: int, db: Session = Depends(get_db)):
post = db.query(Post).filter(Post.id == post_id).first()
if not post:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Post not found")
db.delete(post)
db.commit()
return
This file defines the following endpoints:
Note that you'll need to define the `Post` model and `PostCreate` and `PostResponse` schemas in separate files (`models.py` and `schemas.py`, respectively) for this code to work.

59
app/api/v1/routes/tags.py Normal file
View File

@ -0,0 +1,59 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.db import get_db
from app import models, schemas
router = APIRouter(
prefix="/tags",
tags=["Tags"],
@router.get("/", response_model=List[schemas.Tag])
def get_tags(db: Session = Depends(get_db)):
tags = db.query(models.Tag).all()
return tags
@router.get("/{tag_id}", response_model=schemas.Tag)
def get_tag(tag_id: int, db: Session = Depends(get_db)):
tag = db.query(models.Tag).filter(models.Tag.id == tag_id).first()
if not tag:
raise HTTPException(status_code=404, detail="Tag not found")
return tag
@router.post("/", response_model=schemas.Tag)
def create_tag(tag: schemas.TagCreate, db: Session = Depends(get_db)):
new_tag = models.Tag(name=tag.name)
db.add(new_tag)
db.commit()
db.refresh(new_tag)
return new_tag
@router.put("/{tag_id}", response_model=schemas.Tag)
def update_tag(tag_id: int, tag: schemas.TagUpdate, db: Session = Depends(get_db)):
db_tag = db.query(models.Tag).filter(models.Tag.id == tag_id).first()
if not db_tag:
raise HTTPException(status_code=404, detail="Tag not found")
update_data = tag.dict(exclude_unset=True)
for key, value in update_data.items():
setattr(db_tag, key, value)
db.commit()
db.refresh(db_tag)
return db_tag
@router.delete("/{tag_id}", status_code=204)
def delete_tag(tag_id: int, db: Session = Depends(get_db)):
tag = db.query(models.Tag).filter(models.Tag.id == tag_id).first()
if not tag:
raise HTTPException(status_code=404, detail="Tag not found")
db.delete(tag)
db.commit()
return None
This code defines a set of CRUD (Create, Read, Update, Delete) endpoints for managing tags in the `blog_app_svkgt` FastAPI application. Here's a breakdown of the endpoints:
The code uses SQLAlchemy models and Pydantic schemas (not shown in the provided code) to interact with the database and validate the request data, respectively. The `get_db` dependency is used to obtain a database session for each request.

60
app/api/v1/routes/user.py Normal file
View File

@ -0,0 +1,60 @@
from typing import List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.db import get_db
from app.models import User
from app.schemas import UserCreate, UserRead, UserUpdate
router = APIRouter()
@router.post("/users", response_model=UserRead)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = User(**user.dict())
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@router.get("/users", response_model=List[UserRead])
def read_users(db: Session = Depends(get_db)):
users = db.query(User).all()
return users
@router.get("/users/{user_id}", response_model=UserRead)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = db.query(User).get(user_id)
if not db_user:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@router.put("/users/{user_id}", response_model=UserRead)
def update_user(user_id: int, user: UserUpdate, db: Session = Depends(get_db)):
db_user = db.query(User).get(user_id)
if not db_user:
raise HTTPException(status_code=404, detail="User not found")
update_data = user.dict(exclude_unset=True)
for key, value in update_data.items():
setattr(db_user, key, value)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
@router.delete("/users/{user_id}")
def delete_user(user_id: int, db: Session = Depends(get_db)):
db_user = db.query(User).get(user_id)
if not db_user:
raise HTTPException(status_code=404, detail="User not found")
db.delete(db_user)
db.commit()
return {"message": "User deleted successfully"}
This code defines a `router` object using `APIRouter` from FastAPI. It contains the following endpoints:
Note that this code assumes the existence of the following components:

View File

@ -0,0 +1,38 @@
Here's the `comments.py` file with Pydantic schemas for comments, following the FastAPI project structure with SQLite and SQLAlchemy:
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
from app.db.models import Comment
class CommentBase(BaseModel):
body: str
class CommentCreate(CommentBase):
post_id: int
class CommentUpdate(CommentBase):
pass
class CommentInDBBase(CommentBase):
id: int
post_id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
orm_mode = True
class Comment(CommentInDBBase):
pass
class CommentInDB(CommentInDBBase):
pass
Here's a breakdown of the file:
2. `CommentBase` is a Pydantic model representing the base fields of a comment (body).
5. `CommentInDBBase` inherits from `CommentBase` and includes additional fields (`id`, `post_id`, `created_at`, and `updated_at`) that are typically stored in the database. The `orm_mode` config is set to `True` to allow reading data from an ORM model.

View File

@ -0,0 +1,39 @@
Here's the `posts.py` file with Pydantic schemas for posts, located in the `app/api/v1/schemas/` directory:
from typing import Optional
from pydantic import BaseModel
from datetime import datetime
class PostBase(BaseModel):
title: str
content: str
class PostCreate(PostBase):
pass
class PostUpdate(PostBase):
pass
class PostInDBBase(PostBase):
id: int
created_at: datetime
updated_at: Optional[datetime] = None
class Config:
orm_mode = True
class Post(PostInDBBase):
pass
class PostList(BaseModel):
__root__: list[Post]
Explanation:
1. We import the necessary modules: `typing` for type annotations, `pydantic` for defining data models, and `datetime` for working with date and time objects.
2. `PostBase` is a Pydantic model that defines the base fields for a post: `title` and `content`.
5. `PostInDBBase` inherits from `PostBase` and adds additional fields: `id`, `created_at`, and `updated_at`. The `orm_mode` config is set to `True` to allow Pydantic to read data from SQLAlchemy models.

View File

@ -0,0 +1,34 @@
Here's the `tags.py` file with Pydantic schemas for tags in the `app/api/v1/schemas/` directory:
from typing import Optional
from pydantic import BaseModel
class TagBase(BaseModel):
name: str
class TagCreate(TagBase):
pass
class TagUpdate(TagBase):
name: Optional[str] = None
class TagInDBBase(TagBase):
id: int
name: str
class Config:
orm_mode = True
class Tag(TagInDBBase):
pass
class TagInDB(TagInDBBase):
pass
Explanation:
5. `TagInDBBase` inherits from `TagBase` and adds an `id` field as a required integer. It also sets the `orm_mode` configuration to `True`, which allows Pydantic to handle data from an ORM (SQLAlchemy).

View File

@ -0,0 +1,35 @@
Here's the `user.py` file with Pydantic schemas for the `User` model, which you can place in the `app/api/v1/schemas/` directory:
from typing import Optional
from pydantic import BaseModel, EmailStr
class UserBase(BaseModel):
email: Optional[EmailStr] = None
is_active: Optional[bool] = True
is_superuser: bool = False
full_name: Optional[str] = None
class UserCreate(UserBase):
email: EmailStr
password: str
class UserUpdate(UserBase):
password: Optional[str] = None
class UserInDBBase(UserBase):
id: Optional[int] = None
class Config:
orm_mode = True
class User(UserInDBBase):
pass
class UserInUpdate(UserUpdate):
pass
This file defines several Pydantic models for the `User` model:
Note: This code assumes that you have the `pydantic` library installed and that your project follows the recommended structure for FastAPI applications using SQLAlchemy and SQLite.

15
main.py
View File

@ -1,7 +1,10 @@
from fastapi import FastAPI from fastapi import FastAPI
from app.api.db.database import engine, Base
app = FastAPI(title="Generated Backend") from app.api.v1.routes import router
from app.api.core.middleware.activity_tracker import ActivityTrackerMiddleware
@app.get("/") app = FastAPI()
def read_root(): app.add_middleware(ActivityTrackerMiddleware)
return {"message": "Welcome to the generated backend"} app.include_router(router, prefix='/v1')
@app.on_event('startup')
def startup():
Base.metadata.create_all(bind=engine)

View File

@ -1,4 +1 @@
fastapi Here's the `requirements.txt` file for the 'blog_app' FastAPI backend:
uvicorn
sqlalchemy
pydantic