The ESP Website includes numerous utility functions and customizations that simplify common tasks. These utilities handle rendering, templates, testing, and various helper operations.
Rendering and Templates
render_to_response
Use the ESP-specific render_to_response instead of Django’s default. It automatically injects useful context variables:
from esp.utils.web import render_to_response
def my_view ( request ):
context = {
'students' : Student.objects.filter( program = program),
'classes' : ClassSubject.objects.all(),
}
return render_to_response(
'myapp/template.html' ,
request,
context
)
Automatically injected context:
navbar_list: Navigation bar items
theme: Theme settings from ThemeController
current_theme_version: Theme version tag
current_logo_version: Logo version tag
current_header_version: Header version tag
current_favicon_version: Favicon version tag
settings: Django settings object
current_programs: Active programs list
request: The request object (when use_request_context=True)
Template Inheritance
Almost all templates should inherit from the main layout:
{% extends "main.html" %}
{% block content %}
<h1>My Page Title</h1>
<p>Page content goes here...</p>
{% endblock content %}
For minimal pages without the main layout:
{% extends "elements/html" %}
{% block body %}
<!-- Gets standard JS/CSS without navbar/footer -->
{% endblock body %}
Inline QSD (Quasi-Static Data)
Create editable content blocks that admins can modify:
{% load render_qsd %}
{# Simple inline block #}
{% render_inline_qsd "welcome_message" %}
{# With default content #}
{% inline_qsd_block "welcome_message" %}
<h2>Welcome to our program!</h2>
<p>This is the default welcome message.</p>
{% end_inline_qsd_block %}
{# Program-specific block #}
{% render_inline_program_qsd program "program_description" %}
{# Program-specific with default #}
{% inline_program_qsd_block program "rules" %}
<h3>Program Rules</h3>
<ul>
<li>Be respectful</li>
<li>Have fun!</li>
</ul>
{% end_inline_program_qsd_block %}
In some templates, you may need to use prog instead of program depending on the context variable name.
TemplateOverride Model
Override templates by storing them in the database:
from esp.utils.models import TemplateOverride
# Create a template override
TemplateOverride.objects.create(
name = 'program/class_list.html' ,
content = """{% extends "main.html" %}
{% block content %}
<h1>Custom Class List</h1>
<!-- custom template content -->
{% endblock %}
""" ,
version = 1
)
This is commonly used for:
Theme editor customizations
Site-specific printables
Per-site template variations
Template overrides can make debugging difficult. Use sparingly and document their existence.
Helper Functions
get_from_id
Safely retrieve objects by ID with error handling:
from esp.utils.web import get_from_id
from esp.program.models import Program
# Get program by ID, raise ESPError if not found
program = get_from_id(
request. GET .get( 'program_id' ),
Program,
strtype = 'program' ,
error = True
)
# Get program by ID, return None if not found
program = get_from_id(
request. GET .get( 'program_id' ),
Program,
error = False
)
if program is None :
# handle missing program
pass
esp_context_stuff
Get common ESP context variables:
from esp.utils.web import esp_context_stuff
context = esp_context_stuff()
# Returns dict with theme, settings, current_programs, etc.
Useful when building context manually outside of render_to_response.
Testing
Manual Testing
Always test features manually on your dev server before pushing to production. Seriously, do it.
The Django Debug Toolbar automatically appears on dev servers:
Shows SQL queries and execution time
Displays template rendering information
Shows cache operations
Helps debug performance issues
Reveals context variables
Automated Tests
Run the test suite:
# Run all tests
./manage.py test
# Run specific app tests
./manage.py test esp.program
# Run specific test class
./manage.py test esp.program.tests.ProgramTests
# Run specific test method
./manage.py test esp.program.tests.ProgramTests.test_creation
Tests are automatically run by Travis CI when you push to GitHub. Write more tests to improve coverage!
Utility Modules
The esp/esp/utils/ directory contains many utility modules:
web.py
from esp.utils.web import render_to_response, get_from_id
Rendering helpers and HTTP utilities.
decorators.py
from esp.utils.decorators import cached_property
class MyClass :
@cached_property
def expensive_property ( self ):
# Computed once, then cached
return expensive_computation()
Custom form fields and widgets:
from esp.utils.forms import NameField, PhoneNumberField
from esp.utils.widgets import DateTimeWidget
query_utils.py
Database query helpers:
from esp.utils.query_utils import nest_Q
# Combine Q objects efficiently
combined_q = nest_Q(q_objects, 'OR' )
sanitize.py
HTML sanitization for user input:
from esp.utils.sanitize import sanitize_html
# Clean user-submitted HTML
safe_html = sanitize_html(user_input)
template.py
Template loading and caching utilities.
latex.py
LaTeX generation helpers:
from esp.utils.latex import render_to_latex
# Generate PDF from template
pdf = render_to_latex( 'certificates/template.tex' , context)
Custom template tags in esp/esp/utils/templatetags/:
markup.py
{% load markup %}
{{ content|markdown }}
{{ content|restructuredtext }}
query_builder.py
{% load query_builder %}
{% build_query program=program grade=9 %}
Management Commands
Custom management commands in esp/esp/utils/management/commands/:
# Flush cache
./manage.py flushcache
# Clean compiled Python files
./manage.py clean_pyc
# Install dependencies
./manage.py install
# Update database and static files
./manage.py update
Error Handling
ESPError
Raise user-friendly errors:
from esp.middleware import ESPError
def my_view ( request ):
if not user.is_admin():
raise ESPError(
'You must be an administrator to access this page.' ,
log = False # Don't log to error tracking
)
Custom Error Views
ESP overrides Django’s error views to provide context:
# In esp/utils/web.py
def error404 ( request , exception = None , template_name = '404.html' ):
context = {
'request_path' : request.path,
'DEFAULT_EMAIL_ADDRESSES' : settings. DEFAULT_EMAIL_ADDRESSES
}
return render_to_response(template_name, request, context)
Best Practices
Always Use render_to_response
Use ESP’s render_to_response instead of Django’s to ensure proper context: # GOOD
from esp.utils.web import render_to_response
return render_to_response( 'template.html' , request, context)
# BAD - missing ESP context
from django.shortcuts import render
return render(request, 'template.html' , context)
Use template inheritance for consistent layout: {% extends "main.html" %}
{% block content %}
<!-- your content -->
{% endblock %}
Test manually on dev server
Check the debug toolbar for performance issues
Write automated tests for new features
Run existing tests to ensure no regressions
Use QSD for Admin-Editable Content
Let admins edit content without code changes: {% load render_qsd %}
{% inline_program_qsd_block program "custom_rules" %}
Default rules content
{% end_inline_program_qsd_block %}
Document Template Overrides
If using TemplateOverride, document it clearly: # Create override with comment
TemplateOverride.objects.create(
name = 'program/printables/schedule.html' ,
content = custom_template,
version = 1
)
Common Patterns
View with Context
from esp.utils.web import render_to_response
from esp.program.models import Program
def program_list ( request ):
programs = Program.objects.filter( anchor__parent__isnull = True )
context = {
'programs' : programs,
'nav_category' : 'manage' ,
}
return render_to_response(
'program/program_list.html' ,
request,
context
)
Template with QSD
{% extends "main.html" %}
{% load render_qsd %}
{% block content %}
<h1>{{ program.niceName }}</h1>
{% inline_program_qsd_block program "welcome_message" %}
<p>Welcome to {{ program.niceName }}!</p>
{% end_inline_program_qsd_block %}
<div class="class-list">
{% for cls in classes %}
<div class="class">{{ cls.title }}</div>
{% endfor %}
</div>
{% endblock %}
from django import forms
from esp.utils.forms import NameField, PhoneNumberField
from esp.utils.widgets import DateTimeWidget
class RegistrationForm ( forms . Form ):
name = NameField()
phone = PhoneNumberField( required = False )
preferred_time = forms.DateTimeField(
widget = DateTimeWidget(),
required = False
)
Additional Resources
Rendering utilities: esp/esp/utils/web.py
Template model: esp/esp/utils/models.py
Custom decorators: esp/esp/utils/decorators.py
Form utilities: esp/esp/utils/forms.py
Template tags: esp/esp/utils/templatetags/