The create:crud command automatically generates a fully functional CRUD (Create, Read, Update, Delete) controller for an existing entity.
Usage
framefox create:crud [entity_name]
Arguments
The name of the entity in snake_case. The entity and its repository must already exist.
Prerequisites
Before creating a CRUD controller:
Entity must exist - Create with framefox create:entity
Repository must exist - Auto-created with entity
Database must be set up - Run migrations
CRUD Types
Choose between two types:
1. API CRUD Controller
JSON-based REST API with full CRUD operations.
Features:
List all records (GET)
Get single record (GET)
Create record (POST)
Update record (PUT)
Delete record (DELETE)
JSON responses
Automatic serialization
2. Templated CRUD Controller
Server-side rendered HTML pages with forms.
Features:
List view with table
Detail view
Create form
Edit form
Delete confirmation
Form validation
Auto-generated templates
Example Session
$ framefox create:crud
What is the name of the entity you want to create a CRUD with ? ( snake_case )
Entity name: user
What type of controller do you want to create?
1. API CRUD controller
2. Templated CRUD controller
CRUD controller type: 1
✓ CRUD Controller created successfully: src/controller/user_controller.py
API CRUD Controller
Generated Controller
Creates src/controller/user_controller.py:
from framefox.core.di.decorator.service import service
from framefox.core.http.request import Request
from framefox.core.http.response import Response
from framefox.core.routing.decorator.route import route
from src.entity.user import User
from src.repository.user_repository import UserRepository
@service
class UserController :
def __init__ ( self , user_repository : UserRepository):
self .user_repository = user_repository
@route ( "/api/user" , methods = [ "GET" ])
def index ( self ) -> Response:
"""List all users"""
users = self .user_repository.find_all()
return Response.json([
user.to_dict() for user in users
])
@route ( "/api/user/<int:id>" , methods = [ "GET" ])
def show ( self , id : int ) -> Response:
"""Get a specific user"""
user = self .user_repository.find( id )
if not user:
return Response.json(
{ "error" : "User not found" },
status = 404
)
return Response.json(user.to_dict())
@route ( "/api/user" , methods = [ "POST" ])
def create ( self , request : Request) -> Response:
"""Create a new user"""
data = request.json()
user = User( ** data)
self .user_repository.save(user)
return Response.json(
user.to_dict(),
status = 201
)
@route ( "/api/user/<int:id>" , methods = [ "PUT" ])
def update ( self , id : int , request : Request) -> Response:
"""Update a user"""
user = self .user_repository.find( id )
if not user:
return Response.json(
{ "error" : "User not found" },
status = 404
)
data = request.json()
for key, value in data.items():
setattr (user, key, value)
self .user_repository.save(user)
return Response.json(user.to_dict())
@route ( "/api/user/<int:id>" , methods = [ "DELETE" ])
def delete ( self , id : int ) -> Response:
"""Delete a user"""
user = self .user_repository.find( id )
if not user:
return Response.json(
{ "error" : "User not found" },
status = 404
)
self .user_repository.delete(user)
return Response.json(
{ "message" : "User deleted" },
status = 200
)
API Endpoints
Method Endpoint Description GET /api/userList all users GET /api/user/{id}Get user by ID POST /api/userCreate new user PUT /api/user/{id}Update user DELETE /api/user/{id}Delete user
Testing API Endpoints
# List all users
curl http://localhost:8000/api/user
# Get user by ID
curl http://localhost:8000/api/user/1
# Create user
curl -X POST http://localhost:8000/api/user \
-H "Content-Type: application/json" \
-d '{"username":"john","email":"[email protected] "}'
# Update user
curl -X PUT http://localhost:8000/api/user/1 \
-H "Content-Type: application/json" \
-d '{"username":"john_updated"}'
# Delete user
curl -X DELETE http://localhost:8000/api/user/1
Templated CRUD Controller
Generated Files
Creates multiple files:
Controller : src/controller/user_controller.py
Form Type : src/form/user_type.py
Templates : templates/user/
index.html - List view
create.html - Create form
read.html - Detail view
update.html - Edit form
Generated Controller
from framefox.core.di.decorator.service import service
from framefox.core.http.request import Request
from framefox.core.http.response import Response
from framefox.core.routing.decorator.route import route
from src.entity.user import User
from src.form.user_type import UserType
from src.repository.user_repository import UserRepository
@service
class UserController :
def __init__ ( self , user_repository : UserRepository):
self .user_repository = user_repository
@route ( "/user" , methods = [ "GET" ])
def index ( self ) -> Response:
"""List all users"""
users = self .user_repository.find_all()
return Response.render( "user/index.html" , {
"users" : users
})
@route ( "/user/<int:id>" , methods = [ "GET" ])
def show ( self , id : int ) -> Response:
"""Show user details"""
user = self .user_repository.find( id )
if not user:
return Response.redirect( "/user" )
return Response.render( "user/read.html" , {
"user" : user
})
@route ( "/user/create" , methods = [ "GET" ])
def create ( self ) -> Response:
"""Show create form"""
form = UserType()
return Response.render( "user/create.html" , {
"form" : form
})
@route ( "/user" , methods = [ "POST" ])
def store ( self , request : Request) -> Response:
"""Store new user"""
form = UserType(request.form())
if form.is_valid():
user = User( ** form.get_data())
self .user_repository.save(user)
return Response.redirect( "/user" )
return Response.render( "user/create.html" , {
"form" : form
})
@route ( "/user/<int:id>/edit" , methods = [ "GET" ])
def edit ( self , id : int ) -> Response:
"""Show edit form"""
user = self .user_repository.find( id )
if not user:
return Response.redirect( "/user" )
form = UserType( data = user.to_dict())
return Response.render( "user/update.html" , {
"user" : user,
"form" : form
})
@route ( "/user/<int:id>" , methods = [ "POST" ])
def update ( self , id : int , request : Request) -> Response:
"""Update user"""
user = self .user_repository.find( id )
if not user:
return Response.redirect( "/user" )
form = UserType(request.form())
if form.is_valid():
for key, value in form.get_data().items():
setattr (user, key, value)
self .user_repository.save(user)
return Response.redirect( f "/user/ { id } " )
return Response.render( "user/update.html" , {
"user" : user,
"form" : form
})
@route ( "/user/<int:id>/delete" , methods = [ "POST" ])
def delete ( self , id : int ) -> Response:
"""Delete user"""
user = self .user_repository.find( id )
if user:
self .user_repository.delete(user)
return Response.redirect( "/user" )
src/form/user_type.py:
from framefox.core.form.form_type import FormType
from framefox.core.form.field import StringField, EmailField, BooleanField
from framefox.core.form.validator import Required, Length, Email
class UserType ( FormType ):
username = StringField(
label = "Username" ,
validators = [Required(), Length( min = 3 , max = 100 )]
)
email = EmailField(
label = "Email" ,
validators = [Required(), Email()]
)
is_active = BooleanField(
label = "Active" ,
default = True
)
Generated Templates
List view (templates/user/index.html):
{% extends "base.html" %}
{% block title %}Users{% endblock %}
{% block content %}
< div class = "container" >
< h1 > Users </ h1 >
< a href = "/user/create" class = "btn btn-primary" > Create New User </ a >
< table class = "table" >
< thead >
< tr >
< th > ID </ th >
< th > Username </ th >
< th > Email </ th >
< th > Actions </ th >
</ tr >
</ thead >
< tbody >
{% for user in users %}
< tr >
< td > {{ user.id }} </ td >
< td > {{ user.username }} </ td >
< td > {{ user.email }} </ td >
< td >
< a href = "/user/{{ user.id }}" > View </ a >
< a href = "/user/{{ user.id }}/edit" > Edit </ a >
< form method = "POST" action = "/user/{{ user.id }}/delete" style = "display:inline" >
< button type = "submit" > Delete </ button >
</ form >
</ td >
</ tr >
{% endfor %}
</ tbody >
</ table >
</ div >
{% endblock %}
Validation
The command checks:
Entity Exists - Must be created first
Repository Exists - Must be created with entity
Controller Doesn’t Exist - Prevents overwriting
Error Examples
# Entity doesn't exist
$ framefox create:crud product
Failed to create controller. Entity or repository does not exist.
# Controller already exists
$ framefox create:crud user
Controller user already exists!
Customization
After generation, you can:
Add Validation - Enhance form validators
Add Filters - Filter list results
Add Pagination - Paginate list view
Add Search - Search functionality
Add Authorization - Protect endpoints
Customize Templates - Modify HTML/CSS
Best Practices
Use API controllers for SPAs and mobile apps
Use templated controllers for admin panels
Add authentication before exposing CRUD endpoints
Implement soft deletes instead of hard deletes
Add pagination for large datasets
Validate all input data
Use transactions for data integrity
Next Steps
Add Authentication Secure your CRUD endpoints
Test Routes View generated routes