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/
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')
โ DON'T
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'
Always Filter by User
โ DO
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
โ DON'T
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
Template Naming
โ DO
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)
UUID Primary Keys (When Appropriate)
import uuid
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" %}
Delete
Emoji Usage
โ DO
{% block body_title %}๐ Notes{% endblock %}
๐ Home
{% retro_button "โ 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>
Mobile Touch Targets
.retro-btn {
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;
}
}
Window Titles
.retro-window-title {
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
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
)
๐ Security Practices
Row-Level Security
โ DO
# 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)
Never Expose Secrets
โ DON'T
SECRET_KEY = 'hardcoded-secret-key'
API_KEY = 'my-api-key-12345'
โ DO
SECRET_KEY = env('SECRET_KEY')
API_KEY = env('API_KEY')
๐ Additional Resources