Overview
AbstractController is the base class for all controllers in Framefox. It provides essential methods for rendering templates, returning JSON responses, handling redirects, managing flash messages, and interacting with forms and authentication.
All your controllers should extend this class to inherit these capabilities.
Usage
from framefox.core.controller.abstract_controller import AbstractController
class UserController(AbstractController):
def index(self):
users = self.get_users()
return self.render('user/index.html', {'users': users})
def create(self, request):
user = User()
form = self.create_form(UserForm, user)
if form.is_valid():
# Save user
self.flash('success', 'User created successfully')
return self.redirect(self.generate_url('user_list'))
return self.render('user/create.html', {'form': form})
Constructor
__init__()
Initializes a new controller instance with dependency injection container and template renderer.
Note: Child controllers can override this method to inject their own dependencies.
Example:
class CustomController(AbstractController):
def __init__(self):
super().__init__()
self.my_service = self._container.get_by_name("MyService")
Methods
render()
Renders a template file with optional context data and returns an HTML response.
def render(self, template_path, context=None)
Path to the template file to render (relative to your templates directory)
Dictionary of data to pass to the template for rendering
The rendered template as an HTML response
Example:
def show_profile(self, user_id: int):
user = get_user_by_id(user_id)
return self.render('profile/show.html', {
'user': user,
'last_login': user.last_login
})
json()
Returns data as a JSON response with an optional HTTP status code.
def json(self, data: dict, status: int = 200)
The dictionary data to serialize as JSON
HTTP status code for the response
The data serialized as a JSON response
Example:
def api_get_users(self):
users = User.query.all()
return self.json({
'users': [u.to_dict() for u in users],
'count': len(users)
})
def api_create_user(self, request):
# Validation failed
return self.json({
'error': 'Invalid email address'
}, status=400)
redirect()
Redirects the user to a specific URL with an optional HTTP status code.
def redirect(self, location: str, code: int = 302)
The URL to redirect to (can be absolute or relative)
HTTP status code for the redirect (302 = temporary, 301 = permanent)
A redirect response object
Example:
def delete_user(self, user_id: int):
user = get_user_by_id(user_id)
user.delete()
self.flash('success', 'User deleted successfully')
return self.redirect('/users')
def login(self, request):
# After successful login, permanent redirect
return self.redirect('/dashboard', code=301)
flash()
Adds a flash message to the session for displaying on the next request. Flash messages are typically used to show feedback after form submissions or actions.
def flash(self, category: str, message: str)
The category/type of the flash message (e.g., ‘success’, ‘error’, ‘warning’, ‘info’)
The message content to display to the user
This method does not return a value
Example:
def update_settings(self, request):
settings = get_user_settings()
settings.update(request.data)
settings.save()
self.flash('success', 'Settings updated successfully')
return self.redirect('/settings')
def delete_account(self, user_id: int):
try:
user = get_user_by_id(user_id)
user.delete()
self.flash('success', 'Account deleted')
except Exception as e:
self.flash('error', f'Failed to delete account: {str(e)}')
return self.redirect('/admin/users')
generate_url()
Generates a URL for a named route with optional parameters. This is useful for creating URLs dynamically without hardcoding paths.
def generate_url(self, route_name: str, **params)
The name of the route to generate a URL for
Additional parameters to include in the URL (e.g., path parameters, query strings)
The generated URL as a string
Example:
def show_user(self, user_id: int):
user = get_user_by_id(user_id)
edit_url = self.generate_url('user_edit', user_id=user_id)
return self.render('user/show.html', {
'user': user,
'edit_url': edit_url
})
def redirect_to_profile(self):
user = self.get_user()
profile_url = self.generate_url('user_profile', username=user.username)
return self.redirect(profile_url)
Creates a form instance from a form type class and binds it to an entity object. This is used for handling form creation, validation, and data binding.
def create_form(self, form_type_class, entity_instance)
The form class to instantiate (should be a subclass of your form base class)
The entity object to bind to the form (e.g., a model instance)
The created form instance bound to the entity
Example:
from src.form.user_form import UserForm
from src.entity.user import User
def edit_user(self, request, user_id: int):
user = get_user_by_id(user_id)
form = self.create_form(UserForm, user)
if request.method == 'POST':
form.bind(request.data)
if form.is_valid():
form.save()
self.flash('success', 'User updated successfully')
return self.redirect(self.generate_url('user_list'))
return self.render('user/edit.html', {'form': form})
def create_user(self, request):
user = User() # Empty entity
form = self.create_form(UserForm, user)
if request.method == 'POST':
form.bind(request.data)
if form.is_valid():
form.save()
self.flash('success', 'User created successfully')
return self.redirect(self.generate_url('user_list'))
return self.render('user/create.html', {'form': form})
get_user()
Retrieves the currently authenticated user from the user provider.
def get_user(self, user_class=None)
Optional specific user class to cast the result to
The current authenticated user instance, or None if not authenticated
Example:
def dashboard(self):
user = self.get_user()
if not user:
return self.redirect('/login')
return self.render('dashboard.html', {
'user': user,
'notifications': user.get_notifications()
})
def profile(self):
from src.entity.user import User
user = self.get_user(User)
return self.render('profile.html', {'user': user})
def api_current_user(self):
user = self.get_user()
if not user:
return self.json({'error': 'Not authenticated'}, status=401)
return self.json({
'id': user.id,
'username': user.username,
'email': user.email
})
Complete Example
Here’s a complete example showing multiple methods working together:
from framefox.core.controller.abstract_controller import AbstractController
from src.form.article_form import ArticleForm
from src.entity.article import Article
class ArticleController(AbstractController):
def list(self):
"""Display list of all articles"""
articles = Article.query.all()
return self.render('article/list.html', {
'articles': articles
})
def show(self, article_id: int):
"""Display a single article"""
article = Article.query.get(article_id)
if not article:
self.flash('error', 'Article not found')
return self.redirect(self.generate_url('article_list'))
return self.render('article/show.html', {
'article': article
})
def create(self, request):
"""Create a new article"""
user = self.get_user()
if not user:
return self.redirect('/login')
article = Article()
article.author = user
form = self.create_form(ArticleForm, article)
if request.method == 'POST':
form.bind(request.data)
if form.is_valid():
form.save()
self.flash('success', 'Article created successfully')
return self.redirect(
self.generate_url('article_show', article_id=article.id)
)
return self.render('article/create.html', {'form': form})
def api_list(self):
"""API endpoint returning articles as JSON"""
articles = Article.query.all()
return self.json({
'articles': [a.to_dict() for a in articles],
'count': len(articles)
})
See Also
- ControllerResolver - Learn how controllers are discovered and resolved
- Routing Guide - Learn how to define routes that map to controller methods
- Forms - Learn more about form handling in Framefox