Static File Management
Django applications use CSS, JavaScript, and image assets as static files. Grouping them in a dedicated directory simplifies maintainance. While static directories can live inside each app, placing common assets at the project root is usually cleaner.
Locating Static Files
Define the lookup directories in the project’s settings:
# test5/settings.py
STATIC_URL = '/assets/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
Create a top-level static folder containing img, css, and js subdirectories.
A simple view serves a template that references a static image:
# booktest/views.py
def show_image(request):
return render(request, 'booktest/image_demo.html')
# booktest/urls.py
url(r'^image/$', views.show_image),
Inside templates/booktest/image_demo.html:
<html>
<head>
<title>Static File Demonstration</title>
</head>
<body>
<img src="/assets/img/logo.png"/>
</body>
</html>
Using the Static Template Tag
Hard‑coding asset URLs becomes fragile when deployment configurations change. The static tag resolves the correct path automatically:
{% load static %}
<html>
<head>
<title>Static Tag Example</title>
</head>
<body>
<!-- Works regardless of STATIC_URL -->
<img src="{% static 'img/logo.png' %}"/>
</body>
</html>
For production deployments behind Nginx, static files are usually served directly by the web server rather than through Django; the static tag still produces the correct URL prefix.
Middleware Internals
Middleware is Django’s hook system for processing requests and responses globally. It allows cross‑cutting concerns such as logging, authentication, or header injection without modifying view logic.
Built‑in middleware exposes several stages:
__init__– called once when the server starts.process_request– before the URL dispatcher.process_view– after URL matching, before the view.process_template_response– after a template‑based response is generated.process_response– before the response is sent to the client.process_exception– triggered when a view raises an exception.
Writing a Custom Middleware
Create a file booktest/middleware.py:
class TracingMiddleware:
def __init__(self):
print('-- Middleware initialized')
def process_request(self, request):
print('-- Before request processing')
def process_view(self, request, view_func, view_args, view_kwargs):
print('-- Before calling the view')
def process_template_response(self, request, response):
print('-- After template rendering')
return response
def process_response(self, request, response):
print('-- Response about to be returned')
return response
class ErrorMonitor:
def process_exception(self, request, exception):
print('-- Caught exception:', exception)
Register the middleware in MIDDLEWARE (or MIDDLEWARE_CLASSES) inside settings.py.
Ordering Note: If multiple middleware classes define the same method, the framework processes them in reverse registration order.
Admin Site Customization
The Django admin ganerates CRUD interfaces from model definitions. After creating a superuser with python manage.py createsuperuser, register models in booktest/admin.py:
from django.contrib import admin
from .models import AreaInfo
admin.site.register(AreaInfo)
Controlling List Page Appearance
Use a custom ModelAdmin subclass:
@admin.register(AreaInfo)
class AreaConfig(admin.ModelAdmin):
list_per_page = 15
actions_on_top = True
actions_on_bottom = True
list_display = ['id', 'atitle']
list_filter = ['atitle']
search_fields = ['atitle']
Columns can display model methods:
# models.py
class AreaInfo(models.Model):
atitle = models.CharField(max_length=30)
aParent = models.ForeignKey('self', null=True, blank=True, on_delete=models.SET_NULL)
def display_name(self):
return self.atitle
display_name.short_description = 'Region Name'
display_name.admin_order_field = 'atitle'
Customizing the Edit Form
Group fields or reorder them:
class AreaConfig(admin.ModelAdmin):
fieldsets = (
('Primary', {'fields': ('atitle',)}),
('Hierarchy', {'fields': ('aParent',)}),
)
Inline editing for related objects uses StackedInline or TabularInline:
class SubAreaInline(admin.TabularInline):
model = AreaInfo
extra = 2
class AreaConfig(admin.ModelAdmin):
inlines = [SubAreaInline]
Overriding Admin Templates
Copy the original Django admin template (e.g., base_site.html) into your project’s templates/admin directory and modify it:
{% extends "admin/base.html" %}
{% block branding %}
<h1>Custom Administration Panel</h1>
{% endblock %}
Image Uploads
Django stores uploaded images on the filesystem and records their paths in the database. First install Pillow:
pip install Pillow
Define a model with an ImageField:
class Pictest(models.Model):
image = models.ImageField(upload_to='booktest/')
Add a media storage location in settings.py:
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/media')
Via Admin Interface
Register the model; the admin automatically provides file picker widgets.
Via Custom Form
Create a form that sends multipart/form-data:
<form method="post" action="/save_picture/" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="img">
<button type="submit">Upload</button>
</form>
Handle the file in a view:
from django.core.files.storage import FileSystemStorage
def save_picture(request):
uploaded = request.FILES['img']
storage = FileSystemStorage()
saved_name = storage.save(f'booktest/{uploaded.name}', uploaded)
Pictest.objects.create(image=saved_name)
return HttpResponse('Image saved')
To display the picture, pass the object to a template and build the URL with the media prefix:
<img src="/static/media/{{ pictest.image }}">
Pagination
Django’s Paginator (in django.core.paginator) splits large query results into pages.
Paginator Usage
from django.core.paginator import Paginator
from .models import AreaInfo
def region_list(request, page_num=1):
all_top_level = AreaInfo.objects.filter(aParent__isnull=True).order_by('id')
paginator = Paginator(all_top_level, 10)
current_page = paginator.page(page_num)
context = {
'regions': current_page,
'page_range': paginator.page_range,
'current_page': page_num,
}
return render(request, 'booktest/region_page.html', context)
Template:
<ul>
{% for area in regions %}
<li>{{ area.id }} – {{ area.atitle }}</li>
{% endfor %}
</ul>
<div>
{% for num in page_range %}
{% if num == current_page %}
<strong>{{ num }}</strong>
{% else %}
<a href="/regions/{{ num }}/">{{ num }}</a>
{% endif %}
{% endfor %}
</div>
Ajax‑Driven Cascading Dropdowns
A common pattern is selecting Province → City → District. jQuery’s $.get sends requests without interfering with Django’s CSRF protection when using cookie‑based tokens.
Front‑end template (area_select.html):
<select id="province"><option value="">Choose province</option></select>
<select id="city"><option value="">Choose city</option></select>
<select id="district"><option value="">Choose district</option></select>
<script src="/assets/js/jquery.min.js"></script>
<script>
$(function() {
$.get('/api/provinces/', function(resp) {
$.each(resp.data, function(_, prov) {
$('#province').append(`<option value="${prov.id}">${prov.name}</option>`);
});
});
$('#province').change(function() {
$.get('/api/children/' + this.value + '/', function(resp) {
let citySelect = $('#city').empty().append('<option>Select city</option>');
$('#district').empty().append('<option>Select district</option>');
$.each(resp.data, function(_, city) {
citySelect.append(`<option value="${city.id}">${city.name}</option>`);
});
});
});
$('#city').change(function() {
$.get('/api/children/' + this.value + '/', function(resp) {
let distSelect = $('#district').empty().append('<option>Select district</option>');
$.each(resp.data, function(_, district) {
distSelect.append(`<option value="${district.id}">${district.name}</option>`);
});
});
});
});
</script>
Back‑end endpoints:
from django.http import JsonResponse
from .models import AreaInfo
def api_provinces(request):
provinces = AreaInfo.objects.filter(aParent__isnull=True)
payload = [{'id': p.id, 'name': p.atitle} for p in provinces]
return JsonResponse({'data': payload})
def api_children(request, parent_id):
children = AreaInfo.objects.filter(aParent_id=parent_id)
payload = [{'id': c.id, 'name': c.atitle} for c in children]
return JsonResponse({'data': payload})
Register the URLs and the cascading selection is ready.