Coding Standards & Style Guide
Q9 Coding Standards & Style Guide
Overview
This guide documents the coding standards and conventions used throughout the Q9 project. Following these standards ensures consistency, maintainability, and a cohesive user experience.
URL Conventions
REST-style URLs
✓ DO
/projects/
/projects/123/
/projects/123/workers/
/tasks/
/contacts/
/projects/
/projects/123/
/projects/123/workers/
/tasks/
/contacts/
Use Underscores (NOT Hyphens)
✓ DO
path('task_create/', views.TaskCreateView.as_view(), name='task_create')
path('contact_list/', views.ContactListView.as_view(), name='contact_list')
path('task_create/', views.TaskCreateView.as_view(), name='task_create')
path('contact_list/', views.ContactListView.as_view(), name='contact_list')
✗ DON'T
path('task-create/', ...)
path('contact-list/', ...)
path('task-create/', ...)
path('contact-list/', ...)
URL Namespacing
✓ DO
# In app's urls.py
app_name = 'events'
# In templates
{% url 'events:event_list' %}
{% url 'events:event_create' %}
View Patterns
Class-Based Views for CRUD
✓ DO
class ContactListView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = Contact
table_class = ContactTable
filterset_class = ContactFilter
template_name = 'contacts/contact_list.html'
class ContactListView(LoginRequiredMixin, SingleTableMixin, FilterView):
model = Contact
table_class = ContactTable
filterset_class = ContactFilter
template_name = 'contacts/contact_list.html'
Always Filter by User
✓ DO
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
✗ DON'T
def get_queryset(self):
return Task.objects.all() # Security risk!
def get_queryset(self):
return Task.objects.all() # Security risk!
Template Organization
Directory Structure
app_name/templates/app_name/
├── model_list.html
├── model_detail.html
├── model_form.html (for create/edit)
└── partials/
└── _model_row.html
├── model_list.html
├── model_detail.html
├── model_form.html (for create/edit)
└── partials/
└── _model_row.html
Template Naming
✓ DO
contacts/contact_list.html
contacts/contact_detail.html
contacts/contact_form.html
contacts/partials/_contact_row.html
contacts/contact_list.html
contacts/contact_detail.html
contacts/contact_form.html
contacts/partials/_contact_row.html
Model Conventions
Required Fields
✓ DO
class Contact(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Optional project association
project = models.ForeignKey(Project, null=True, blank=True)
class Contact(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Optional project association
project = models.ForeignKey(Project, null=True, blank=True)
UUID Primary Keys (When Appropriate)
import uuid
class File(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class File(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
UI Components
Retro Windows 95 Style
✓ DO
{% retro_window "My Notes" %}
{% render_table table %}
{% endretro_window %}
Button Styling
✓ DO
{% retro_button "Save" btn_type="primary" %}
{% retro_link_button url "Edit" btn_type="secondary" %}
Icon Usage
✓ DO
{% block body_title %}Notes{% endblock %}
Home
{% retro_button "Add New" %}
{# Use Font Awesome icons where needed #}
Add New
Responsive Design
Column Classes
✓ DO
<div class="retro-col-12 retro-col-md-8">Content</div>
<div class="retro-col-12 retro-col-md-4">Sidebar</div>
<div class="retro-col-12 retro-col-md-8">Content</div>
<div class="retro-col-12 retro-col-md-4">Sidebar</div>
Mobile Touch Targets
.retro-btn {
min-height: 44px; /* Apple's recommendation */
padding: 10px 16px;
font-size: 16px; /* Prevents iOS zoom */
}
min-height: 44px; /* Apple's recommendation */
padding: 10px 16px;
font-size: 16px; /* Prevents iOS zoom */
}
Table Handling
✓ DO
{% render_table table %}
/* CSS */
.retro-table-wrapper {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
Dark Mode Support
Forum-Style Light Wrapper Approach
@media (prefers-color-scheme: dark) {
main {
background-color: #c0c0c0 !important;
}
.retro-window-content,
.retro-table-wrapper {
background: #ffffff !important;
color: #000000 !important;
}
}
main {
background-color: #c0c0c0 !important;
}
.retro-window-content,
.retro-table-wrapper {
background: #ffffff !important;
color: #000000 !important;
}
}
Window Titles
.retro-window-title {
background: #000080 !important;
color: #ffffff !important;
}
background: #000080 !important;
color: #ffffff !important;
}
Testing Standards
Test Setup
✓ DO
# Set environment variable before running tests
export CELERY_BROKER_URL=redis://localhost:6379/0
python manage.py test --settings=q9.settings.test
# Set environment variable before running tests
export CELERY_BROKER_URL=redis://localhost:6379/0
python manage.py test --settings=q9.settings.test
Test User Creation
✓ DO
from django.contrib.auth import get_user_model
from allauth.account.models import EmailAddress
User = get_user_model()
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
# Create verified email for allauth
EmailAddress.objects.create(
user=self.user,
email=self.user.email,
verified=True,
primary=True
)
from django.contrib.auth import get_user_model
from allauth.account.models import EmailAddress
User = get_user_model()
self.user = User.objects.create_user(
username='testuser',
email='test@example.com',
password='testpass123'
)
# Create verified email for allauth
EmailAddress.objects.create(
user=self.user,
email=self.user.email,
verified=True,
primary=True
)
Security Practices
Row-Level Security
✓ DO
# Always filter by user
queryset = Model.objects.filter(user=request.user)
# Always filter by user
queryset = Model.objects.filter(user=request.user)
Form Security
✓ DO
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
Never Expose Secrets
✗ DON'T
SECRET_KEY = 'hardcoded-secret-key'
API_KEY = 'my-api-key-12345'
SECRET_KEY = 'hardcoded-secret-key'
API_KEY = 'my-api-key-12345'
✓ DO
SECRET_KEY = env('SECRET_KEY')
API_KEY = env('API_KEY')
SECRET_KEY = env('SECRET_KEY')
API_KEY = env('API_KEY')
Additional Resources
- Design System - Interactive component showcase
- Documentation - General documentation
- Django Admin - For superusers