Quickstart¶
Install django-utils2, add it to INSTALLED_APPS, and pick from the
three small features below. Each works on its own and needs no
configuration beyond the app entry. Features that do need a setting,
such as the encrypted fields, name it on their own page.
Install¶
pip install django-utils2
Optional extras, installed with pip install "django-utils2[<extra>]":
Extra |
Installs |
Needed for |
|---|---|---|
|
|
Encrypted model fields: |
Verify the install¶
$ pip install django-utils2 Collecting django-utils2 Installing collected packages: django-utils2 Successfully installed django-utils2-4.1.1 $ python -m django check System check identified no issues (0 silenced).
Add to INSTALLED_APPS¶
INSTALLED_APPS = [
...,
'django_utils',
]
Three features to start with¶
Three small, independent changes, each usable on its own without adopting the rest of the library.
JSON sub-path filter on an admin¶
If a model has a JSONField, filter the changelist on a value nested
inside it without writing a custom SimpleListFilter:
from django.contrib import admin
from django_utils.admin.filters import JSONFieldFilterDropdown
from myapp.models import Sandwich
class SandwichAdmin(admin.ModelAdmin):
list_filter = (
JSONFieldFilterDropdown.create('data__filling'),
)
admin.site.register(Sandwich, SandwichAdmin)
This adds a sidebar filter on data['filling'] to the changelist. No
JOIN, no denormalized column, no hand-written filter class. The whole
filter family is covered in
Select / dropdown / autocomplete filters.
count_columns on an admin¶
Add sortable related-object counts to list_display. A count method
on the admin costs one query per row, and annotate(Count(...))
starts multiplying counts the moment a second relation joins the
query. count_columns avoids both:
from django.contrib import admin
from django_utils.admin.mixins import CountColumnMixin
from myapp.models import Sandwich
class SandwichAdmin(CountColumnMixin, admin.ModelAdmin):
list_display = ('id',)
count_columns = ('review', 'topping')
admin.site.register(Sandwich, SandwichAdmin)
The review_count and topping_count columns sort like any other
column, and each relation is counted in its own subquery, so a second
relation never inflates the first. Details in
Count columns.
query_budget around a hot view¶
Wrap a view to catch N+1 regressions where they actually happen, in
production, not only inside a test's assertNumQueries:
from django.shortcuts import render
from django_utils.query_debug import query_budget
from myapp.models import Sandwich
@query_budget(warn_at=20, raise_at=100)
def sandwich_list(request):
sandwiches = Sandwich.objects.all()
return render(request, 'sandwiches/list.html', {'sandwiches': sandwiches})
Past 20 queries the view logs a warning, past 100 it raises
QueryBudgetExceeded. The counting runs through Django's own
connection.execute_wrapper(), so it costs nothing with DEBUG off.
Details in Query budgets.