Comprehensive guide to using Class-Based Views (CBVs) with the component framework.
- Basic Usage
- Built-in CBVs
- Authentication & Permissions
- Single Component Views
- Page Views
- Mixins
- Customization
- Examples
from component_framework.adapters.django_views import ComponentView
# urls.py
urlpatterns = [
path("components/<str:name>/", ComponentView.as_view()),
]This handles ALL components through a single endpoint.
Function-Based (FBV):
from component_framework.adapters.django_views import component_view
urlpatterns = [
path("components/<str:name>/", component_view),
]Class-Based (CBV):
from component_framework.adapters.django_views import ComponentView
urlpatterns = [
path("components/<str:name>/", ComponentView.as_view()),
]CBVs provide better customization and reusability.
Base class for all component views.
from component_framework.adapters.django_views import ComponentView
class MyComponentView(ComponentView):
http_method_names = ['post'] # Only POST allowed
def get_component_params(self, request, **kwargs):
"""Add custom parameters to components."""
params = super().get_component_params(request, **kwargs)
params['custom_data'] = 'value'
return paramsRequires user authentication.
from component_framework.adapters.django_views import AuthenticatedComponentView
urlpatterns = [
path("components/<str:name>/", AuthenticatedComponentView.as_view()),
]Automatically adds user and user_id to component params.
Requires specific permissions.
from component_framework.adapters.django_views import PermissionComponentView
class AdminComponentView(PermissionComponentView):
permission_required = 'app.change_model'
urlpatterns = [
path("admin/components/<str:name>/", AdminComponentView.as_view()),
]Disables CSRF protection (use with caution).
from component_framework.adapters.django_views import CSRFExemptComponentView
urlpatterns = [
# For HTMX/API endpoints with alternative security
path("api/components/<str:name>/", CSRFExemptComponentView.as_view()),
]View for a specific component only.
from component_framework.adapters.django_views import SingleComponentView
class CounterView(SingleComponentView):
component_name = "counter"
urlpatterns = [
path("counter/", CounterView.as_view()),
]Full page view with component rendering.
from component_framework.adapters.django_views import ComponentPageView
class DashboardView(ComponentPageView):
template_name = "dashboard.html"
components = {
"counter": {"initial": 5},
"form": {},
}
urlpatterns = [
path("dashboard/", DashboardView.as_view()),
]Template usage:
<!-- dashboard.html -->
<div>{{ components.counter.html|safe }}</div>
<div>{{ components.form.html|safe }}</div>from component_framework.adapters.django_views import AuthenticatedComponentView
class UserComponentView(AuthenticatedComponentView):
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['username'] = request.user.username
return paramsfrom component_framework.adapters.django_views import PermissionComponentView
class SecureView(PermissionComponentView):
permission_required = 'app.use_component'from django.contrib.auth.mixins import UserPassesTestMixin
from component_framework.adapters.django_views import ComponentView
class DynamicPermView(ComponentView, UserPassesTestMixin):
def test_func(self):
component_name = self.kwargs['name']
return self.request.user.has_perm(f'app.use_{component_name}')from django.contrib.admin.views.decorators import staff_member_required
from django.utils.decorators import method_decorator
@method_decorator(staff_member_required, name='dispatch')
class StaffComponentView(ComponentView):
passclass OrderEditorView(SingleComponentView):
component_name = "order_editor"
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['pk'] = kwargs.get('order_id')
return params
# urls.py
path("orders/<int:order_id>/edit/", OrderEditorView.as_view()),class FilteredListView(SingleComponentView):
component_name = "customer_list"
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['filter'] = request.GET.get('filter', 'all')
params['page'] = int(request.GET.get('page', 1))
return paramsclass DashboardView(ComponentPageView):
template_name = "dashboard.html"
def get_components(self):
return {
"stats": {},
"recent_orders": {"limit": 10},
"alerts": {"user_id": self.request.user.id},
}class CustomDashboard(ComponentPageView):
template_name = "dashboard.html"
def get_components(self):
# Load user preferences
widgets = self.request.user.preferences.get('dashboard_widgets', [])
components = {}
for widget in widgets:
components[widget['name']] = widget['params']
return componentsclass OrderDetailView(ComponentPageView):
template_name = "order_detail.html"
def get_components(self):
order_id = self.kwargs['pk']
return {
"order_editor": {"pk": order_id},
"order_history": {"order_id": order_id},
}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['order'] = Order.objects.get(pk=self.kwargs['pk'])
return contextAdd response caching.
from component_framework.adapters.django_views import CacheMixin, ComponentView
class CachedView(CacheMixin, ComponentView):
cache_timeout = 300 # 5 minutes
def get_cache_key(self, name, params, state):
user_id = self.request.user.id
return f"component:{name}:{user_id}:{params.get('pk')}"Add rate limiting (requires django-ratelimit).
from component_framework.adapters.django_views import RateLimitMixin, ComponentView
class RateLimitedView(RateLimitMixin, ComponentView):
rate_limit_key = "component"
rate_limit_rate = "10/m"class LoggingMixin:
def dispatch_component(self, component, event, payload, state):
logger.info(f"User {self.request.user} triggered {event}")
return super().dispatch_component(component, event, payload, state)
class LoggedView(LoggingMixin, ComponentView):
passclass CustomParamView(ComponentView):
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
# Add session data
params['session_id'] = request.session.session_key
# Add IP address
params['ip_address'] = self.get_client_ip(request)
# Add custom header
params['api_key'] = request.headers.get('X-API-Key')
return params
def get_client_ip(self, request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
return x_forwarded_for.split(',')[0]
return request.META.get('REMOTE_ADDR')class APIView(ComponentView):
def render_response(self, result):
"""Add API envelope."""
response = {
"success": True,
"data": result,
"timestamp": timezone.now().isoformat(),
}
return JsonResponse(response)class CustomErrorView(ComponentView):
def handle_error(self, error, name):
"""Custom error responses."""
logger.exception(f"Component error: {name}")
if isinstance(error, ValidationError):
return JsonResponse(
{"error": "Validation failed", "details": error.messages},
status=400
)
return JsonResponse(
{"error": "Internal error", "component": name},
status=500
)class AuditedView(ComponentView):
def post_process_result(self, result, component):
"""Log all component changes."""
AuditLog.objects.create(
user=self.request.user,
component=component.__class__.__name__,
action="update",
data=result["state"]
)
return super().post_process_result(result, component)class ProductAdminView(PermissionComponentView, CacheMixin):
permission_required = 'shop.change_product'
cache_timeout = 60
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['is_admin'] = True
params['can_delete'] = request.user.has_perm('shop.delete_product')
return paramsclass UserDashboard(LoginRequiredMixin, ComponentPageView):
template_name = "user/dashboard.html"
def get_components(self):
user = self.request.user
return {
"profile": {"user_id": user.id},
"recent_orders": {"user_id": user.id, "limit": 5},
"recommendations": {"user_id": user.id},
}class CollabView(AuthenticatedComponentView):
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['broadcast'] = True # Enable WebSocket broadcasting
params['room_id'] = kwargs.get('room_id')
return paramsclass TenantView(ComponentView):
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['tenant_id'] = request.tenant.id
return params
def get_component_class(self, name):
"""Filter components by tenant access."""
component_cls = super().get_component_class(name)
if not self.tenant_can_use_component(name):
return None
return component_cls
def tenant_can_use_component(self, name):
return name in self.request.tenant.allowed_componentsComponentView- General purposeAuthenticatedComponentView- User-specific componentsPermissionComponentView- Admin/privileged componentsSingleComponentView- Dedicated component endpointsComponentPageView- Full page rendering
# ✅ Good
def get_component_params(self, request, **kwargs):
params = super().get_component_params(request, **kwargs)
params['user_id'] = request.user.id
return params
# ❌ Bad
def post(self, request, name):
params = {"user_id": 123} # Hardcoded!class SecureView(
LoginRequiredMixin, # Require login
PermissionRequiredMixin, # Check permissions
RateLimitMixin, # Rate limit
ComponentView
):
permission_required = 'app.use_component'
rate_limit_rate = "30/m"class SmartCachedView(CacheMixin, ComponentView):
def get_cache_key(self, name, params, state):
# Cache per user + object
return f"component:{name}:{self.request.user.id}:{params.get('pk')}"
def post(self, request, name, **kwargs):
# Skip cache for mutations
data = self.parse_request_data(request)
if data.get('event') in ['save', 'delete', 'update']:
return ComponentView.post(self, request, name, **kwargs)
return super().post(request, name, **kwargs)| Feature | FBV | CBV |
|---|---|---|
| Simplicity | ✅ Simpler | More complex |
| Reusability | ❌ Limited | ✅ Excellent |
| Customization | ❌ Harder | ✅ Easy |
| Mixins | ❌ No | ✅ Yes |
| Inheritance | ❌ No | ✅ Yes |
| DRY | ❌ More duplication | ✅ Less duplication |
| Learning Curve | ✅ Easier | Steeper |
Use FBV when: Simple, one-off endpoints Use CBV when: Need customization, reusability, or multiple similar endpoints
CBVs provide:
- ✅ Better code reuse
- ✅ Easy customization via inheritance
- ✅ Mixin support
- ✅ Django's built-in auth mixins
- ✅ Cleaner organization
- ✅ Type hints support
- ✅ Testing-friendly
All the power of function-based views, with the flexibility of classes!