This document outlines the conventions and best practices for Django views in this project. These guidelines ensure consistency, maintainability, and proper integration with templates and HTMX.
The project provides several specialized view classes to handle different types of requests and rendering needs. These are located in the apps.public.helpers package. These reusable Django view classes and mixins are designed to simplify common view patterns, particularly for HTMX integration and team/user session management.
Base view class for rendering Django templates with standardized context handling. Use this for standard pages that render full HTML responses.
from apps.public.helpers import MainContentView
class HomeView(MainContentView):
template_name = "pages/home.html"
def get(self, request, *args, **kwargs):
self.context["items"] = Item.objects.all()
return self.render(request)Key features:
- Automatic context initialization
- Simplified template rendering
- Base template selection based on request type
- URL history management for HTMX requests
Specialized view for HTMX requests with support for out-of-band (OOB) updates, URL history management, and complex HTMX interactions.
from apps.public.helpers import HTMXView, TeamSessionMixin
class UserProfileComponent(HTMXView):
template_name = "components/user_profile.html"
oob_templates = {
"sidebar": "components/common/sidebar.html",
"messages": "layout/messages/toast.html"
}
push_url = "/user/profile"
def get(self, request, *args, **kwargs):
self.context["user_data"] = get_user_data(request.user)
return self.render(request)Key features:
- Enforces HTMX-only requests
- Support for multiple template rendering in one response
- Out-of-band (OOB) updates for multiple page elements
- URL history management
- Automatic toast message handling
Base mixin for managing user session state, like tracking login status.
Mixin for views that require team context. It loads the current team from URL parameters or session and ensures proper user access.
from apps.public.helpers import MainContentView, TeamSessionMixin
class TeamSettingsView(TeamSessionMixin, MainContentView):
template_name = "team/settings.html"
def get(self, request, *args, **kwargs):
# self.team is automatically loaded and available
self.context["members"] = self.team.members.all()
return self.render(request)apps/{app_name}/views/: Standard app viewsapps/{app_name}/views/components/: Component views for HTMX interactionsapps/public/helpers/: Reusable view base classes and mixins
- Views should be named descriptively with a
Viewsuffix - HTMX component views should include
Componentin the name - API views should follow RESTful naming conventions
Examples:
UserProfileViewTeamDashboardComponentTeamMemberListView
- Use
MainContentViewfor standard page rendering - Use
HTMXViewfor HTMX-specific components and interactions - Use Django's generic class-based views for simple CRUD operations
- Add
TeamSessionMixinwhen team context is needed
- Import these components from
apps.public.helpers(not from the views directory) - Example:
from apps.public.helpers import MainContentView, HTMXView, TeamSessionMixin
- Initialize context in
__init__orsetupmethods - Add view-specific context in
get_context_dataor directly toself.context - Use descriptive context variable names
- For HTMX views, always validate that the request is coming from HTMX
- Use
HTMXViewfor complex HTMX interactions with OOB - Handle HTMX response headers properly (
HX-Redirect,HX-Trigger, etc.)
- Set
template_nameexplicitly as a class attribute - For complex logic, override
get_template_names()method - Use consistent template paths matching the view's purpose
- Implement appropriate HTTP method handlers (
get,post, etc.) - Validate inputs and handle errors explicitly
- Use Django forms or serializers for data validation
- Return appropriate status codes and responses
For views that need to update multiple parts of the page:
class CreateTeamView(TeamSessionMixin, HTMXView):
template_name = "components/team_form.html"
oob_templates = {
"team_list": "components/team_list.html",
"messages": "messages/toast.html"
}
def post(self, request, *args, **kwargs):
form = TeamForm(request.POST)
if form.is_valid():
team = form.save()
messages.success(request, "Team created successfully!")
self.context["teams"] = request.user.teams.all()
return self.render(request)
self.context["form"] = form
return self.render(request)For HTMX interactions that should update the browser URL:
class TeamDetailComponent(TeamSessionMixin, HTMXView):
template_name = "components/team_detail.html"
def get(self, request, *args, **kwargs):
team_id = kwargs.get("team_id")
self.context["team"] = get_object_or_404(Team, id=team_id)
return self.render(request, push_url=f"/teams/{team_id}/")For regular form submission:
class TeamCreateView(TeamSessionMixin, MainContentView):
template_name = "team/create.html"
def get(self, request, *args, **kwargs):
self.context["form"] = TeamForm()
return self.render(request)
def post(self, request, *args, **kwargs):
form = TeamForm(request.POST)
if form.is_valid():
team = form.save(commit=False)
team.save()
messages.success(request, "Team created successfully!")
return redirect("team:detail", team_id=team.id)
self.context["form"] = form
return self.render(request)For HTMX-based form submission with partial updates:
class TeamCreateComponent(TeamSessionMixin, HTMXView):
template_name = "components/team_form.html"
def get(self, request, *args, **kwargs):
self.context["form"] = TeamForm()
return self.render(request)
def post(self, request, *args, **kwargs):
form = TeamForm(request.POST)
if form.is_valid():
team = form.save()
messages.success(request, "Team created successfully!")
return redirect("team:list")
self.context["form"] = form
return self.render(request)- Use Django's built-in error views for 404, 500, etc.
- For API endpoints, return proper status codes with error details
- Add proper error messages to the Django message framework
- Display user-friendly error messages in templates
- Place view tests in
apps/{app_name}/tests/test_views/ - Test both GET and POST methods
- Verify proper template usage and context variables
- Test form validation and error handling
- For HTMX views, test with proper request headers and verify response content
Example test for an HTMX view:
from django.test import TestCase, Client
class TeamComponentTestCase(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(username="testuser", password="password")
self.client.login(username="testuser", password="password")
def test_team_list_component(self):
response = self.client.get(
"/teams/components/list/",
HTTP_HX_REQUEST="true" # Simulate HTMX request
)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "components/team_list.html")
self.assertContains(response, "team-list-container")- Keep Views Focused: Each view should have a single responsibility
- Use Mixins Wisely: Compose functionality using mixins, but avoid excessive nesting
- Context Consistency: Use consistent context variable naming across views
- Template Coordination: Ensure views provide all the context variables needed by templates
- Error Handling: Handle all possible errors gracefully
- Security: Always validate user permissions and input data
- Documentation: Add docstrings to explain complex view logic
- TEMPLATE_CONVENTIONS.md: Conventions for Django templates used with these views
- Django View Documentation: Official Django documentation on class-based views
- HTMX Documentation: Official HTMX documentation